Migrazione Blobstore GAE a un'applicazione HRD
Se avete intenzione di migrare l'applicazione Google App Engine allo schema "nuovo" High database di replica, allora probabilmente sapete che il processo di migrazione Google non sarà di gestire i file Blobstore.
Qui troverete alcuni script python che vi aiuteranno a spostare i file Blobstore dall'applicazione vecchio a quello nuovo, e correggere tutti i riferimenti nel nuovo database. Come sempre, usatela a vostro rischio e pericolo, non provare a fare qualcosa senza leggere e comprendere gli script, altrimenti si rischia di perdere i vostri dati in modo permanente.
Alcune considerazioni:
- Abbiamo fatto questi script utilizzato nella nostra applicazione molto personale, quindi posso dire che funziona. Tuttavia, ho dovuto modificare gli script originali per renderli più "generico", rimuovere i miei riferimenti e nomi di database, ecc
- Cercherò di descrivere le fasi della migrazione, ma per favore non cercare di fare nulla prima di leggere l'intero articolo prima.
- Ciò non è in alcun modo destinati ad essere una migrazione completamente automatico, infatti, i passaggi deve essere eseguita manualmente uno per uno.
- Gli script non migra file di grandi dimensioni. Abbiamo migrato i file fino a 40MB senza problemi, ma se hai file più grandi di questo, probabilmente fallirà. Per esempio abbiamo provato un file di 300Mb senza successo, il motivo è che il file deve essere scaricato dal app vecchio al nuovo, e c'è un timeout di 1 minuto per le richieste di app, quindi se il download richiede più che fallirà .
Come funziona lo script funziona
Avrete bisogno di mettere lo script nelle applicazioni, sia il nuovo (HRD) e quello vecchio. Espone sostanzialmente quattro gli URL e sarà necessario accedere a due di loro dall'applicazione HRD.
In questo modo potrete scaricare tutti i file blob dall'applicazione vecchio al nuovo, lo script creerà un modello che mantiene una mappa tra i riferimenti vecchi e nuovi.
Il passo finale è quello di migrare i vecchi riferimenti al nuovo sviluppo delle risorse umane nelle banche dati.
Prerequisiti
- Tutti i riferimenti ai file blob nei tuoi modelli devono essere di tipo
blobstore.BlobReferenceProperty, se non sarà necessario modificare in primo luogo. - Migrare i database dal vecchio al app app nuovo sviluppo delle risorse umane utilizzando il metodo standard di migrazione Google .
Passi
- Aggiungere l'URL alla vostra applicazione
def main():
application = webapp.WSGIApplication(
[
# ... Your app URLs here ...
# TODO - REMOVE THIS AFTER MIGRATION
('/mig/__getblob/(.+)', GetBlob),
('/mig/__getblobkeys/?', GetBlobKeys),
('/mig/__migrateblobs/?', MigrateBlobs),
('/mig/__migratereferences/?', MigrateBlobReferences),
], debug=True)
run_wsgi_app(application)
if __name__ == '__main__':
main()
- Quindi aggiungere il codice di migrazione
####
# TEMPORARY BLOBSTORE MIGRATION CODE
####
MODELS = (
# add your (model, blobinfo_field_name) tuples here
("myModel1", "blobinfofield"),
("myModel2", "BlobInfo"),
)
class mig_BlobMig(db.Model):
fname = db.StringProperty()
blobinfo = blobstore.BlobReferenceProperty()
origkey = db.StringProperty()
class MigrateBlobReferences(blobstore_handlers.BlobstoreDownloadHandler):
'''Migrate blob info references'''
def get(self):
import models
tb = dict([(str(blob.origkey), blob.blobinfo) for blob in mig_BlobMig.all()])
for modelname, field in MODELS:
to_put = []
skipped = 0
for obj in db.GqlQuery('SELECT * FROM %s'%modelname):
oldkey = getattr(obj, field)
if oldkey:
oldkey = str(oldkey.key())
if oldkey in tb:
setattr(obj, field, tb[oldkey])
to_put.append(obj)
else:
skipped += 1
db.put(to_put)
self.response.out.write("[%s] - Migrated %d references!<br/>Skipped: %d<br/>\n"%(modelname, len(to_put), skipped))
class MigrateBlobs(blobstore_handlers.BlobstoreDownloadHandler):
'''Migrate blobs from myoldapp to mynewapp'''
def get(self):
import time
from google.appengine.api import files, urlfetch
start_time = time.time()
timed_out = False
migrated_keys = set([str(blob.origkey) for blob in mig_BlobMig.all()])
all_blobs = eval(urlfetch.fetch("https://myoldapp.appspot.com/mig/__getblobkeys", deadline=15.0).content)
all_keys = set(all_blobs.keys())
missing_keys = list(all_keys-migrated_keys)
if not missing_keys:
self.response.out.write("Nothing to migrate!")
return
# Download 20 blobs per round
download_keys = missing_keys[:20]
MAXSIZE = (10*1024*1024) # 10MB
for origkey in download_keys:
blob = all_blobs[origkey]
url = "https://myoldapp.appspot.com/mig/__getblob/%s"%urllib.quote_plus(origkey)
blob_path = files.blobstore.create(mime_type=blob["content_type"], _blobinfo_uploaded_filename=blob["filename"])
fsize = int(blob["size"])
with files.open(blob_path, 'a') as f:
for first_byte in range(0, fsize, MAXSIZE):
last_byte = (first_byte+MAXSIZE-1)
if last_byte>=fsize: last_byte=(fsize-1)
bytes_range = "bytes=%d-%d"%(first_byte,last_byte)
logging.info("Downloading [%s] range [%s] key=[%s]"%(blob["filename"], bytes_range, origkey))
res = urlfetch.fetch(url, deadline=35.0, headers={"Range": bytes_range})
f.write(res.content)
del res.content
del res
if (time.time()-start_time > 40.0):
timed_out = True
break
if timed_out:
self.response.out.write("Stopped due to time limit!<br/>")
break
files.finalize(blob_path)
blob_key = files.blobstore.get_blob_key(blob_path)
blob_info = blobstore.BlobInfo.get(blob_key)
mig_BlobMig(fname=blob["filename"], blobinfo=blob_info, origkey=origkey).put()
logging.info("Successfully downloaded [%s]!!!"%(blob["filename"]))
self.response.out.write("Migrated %s<br/>"%blob["filename"])
self.response.out.write("<br/>SUCCESS!")
class GetBlobKeys(blobstore_handlers.BlobstoreDownloadHandler):
'''Retrieve all Blob Keys, as a JSON string'''
def get(self):
from simplejson.encoder import JSONEncoder
blobs = {}
for blob_info in blobstore.BlobInfo.all():
blobs[str(blob_info.key())] = {
"filename":str(blob_info.filename),
"content_type":str(blob_info.content_type),
"size":int(blob_info.size),
"key":str(blob_info.key()),
}
self.response.headers["Content-type"] = "application/json"
self.response.out.write(JSONEncoder().encode(blobs))
class GetBlob(blobstore_handlers.BlobstoreDownloadHandler):
'''Download a blob given its key'''
def get(self, blobkey):
blobkey = str(urllib.unquote(blobkey))
blob_info = blobstore.BlobInfo.get(blobkey)
if not blob_info:
self.error(404)
return
self.response.headers["Content-type"] = str(blob_info.content_type)
self.send_blob(blob_info, save_as=True)
####
# END OF TEMPORARY BLOBSTORE MIGRATION CODE
####
- Modificare la
MODELSvariabile aggiunta dei modelli e dei campi blob informazioni che è necessario aggiornare - Sostituire
myoldappURL con l'URL effettivo della vostra vecchia applicazione - Sincronizzare il codice su entrambi i apps
- Aprire un browser e puntare a
http://mynewapp.appspot.com/mig/__migrateblobs. Naturalmente,mynewappdeve essere sostituito con il nome della relativa applicazione HRD. Questo scaricherà le macchie dal app vecchio a quello nuovo. Perché c'è un limite di timeout sarà bisogno di chiamare molte volte. ATTENZIONE: è necessario presentare una sola richiesta alla volta, mai chiamare da due browser / schede allo stesso tempo, altrimenti si scherza il database di migrazione!. Quindi, questo passaggio deve essere ben eseguita, una richiesta al momento e attendere che finisca prima di chiamare il prossimo. Ripetere questa operazione fino a visualizzare il messaggioNothing to migrate!. Questo passo può essere molto lento a seconda di quanti file avete nel blobstore e la loro dimensione, potrebbe richiedere molto tempo per ottenere tutti i file scaricati. Sii paziente! - Una volta che tutti i file vengono copiati nella nuova applicazione, puntare il browser a
http://mynewapp.appspot.com/mig/__migratereferences. Deve essere veloce, e deve essere eseguito una sola volta. Tuttavia, non fa male a chiamare più di una volta. - L'ultimo passo, rimuovere il codice di migrazione e gli URL e sincronizzare nuovamente il codice. È inoltre possibile rimuovere il
mig_BlobMigdatabase.
Buona la migrazione.
