Миграция GAE Blobstore к HRD применение
Если вы планируете перенести Google App Engine приложения к "новому" Высокое схему репликации баз данных, то вы наверняка знаете, что процесс миграции Google не будет обрабатывать Blobstore файлов.
Здесь вы найдете некоторые Python скриптов, которые помогут вам двигаться Blobstore файлы из старых приложений с новой, и исправить все ссылки в новой базе данных. Как всегда, используйте на свой страх и риск, не пытайтесь ничего делать без чтения и понимания сценариев, иначе вы можете потерять свои данные навсегда.
Некоторые соображения:
- Мы же использовали эти сценарии в наших собственных приложений, так что я могу сказать, что это работает. Тем не менее, мне пришлось редактировать оригинальные сценарии, чтобы сделать их более "общий", отказаться от ссылок базы данных и имена и т.д.
- Я постараюсь описать этапы миграции, но, пожалуйста, не пытайтесь ничего делать, прежде чем читать всю статью в первую очередь.
- Это отнюдь не предназначены для полностью автоматической миграции, по сути, шаги должны быть выполнены вручную по одному.
- Сценарии не будут мигрировать большие файлы. Мы мигрировали файлы размером до 40 Мб без проблем, но если у вас есть файлы более, что он, вероятно, удастся. Например, мы пытались безуспешно 300Mb файл, причина в том, что файл должен быть загружен из старых приложений с новой, и есть 1 минута ожидания для приложения запросов, так что, если загрузка занимает больше, чем она не будет выполнена .
Как скрипт работает
Вам нужно будет поставить скрипт в своих приложениях, как новые (HRD) и старый. Это в основном предоставляет четыре адреса и вам необходимо получить доступ к двум из них из HRD приложения.
Делая это, вы будете скачивать все файлы, капля из старых приложений на новый, то сценарий будет создать модель, которая поддерживает отображение между старой и новой литературы.
Последним шагом является перенос старых ссылок на новые в HRD баз данных.
Предпосылки
- Все ссылки на файлы капля в модели должны быть типа
blobstore.BlobReferenceProperty, если не необходимо скорректировать их в первую очередь. - Перенос базы данных из старых приложений на новые приложения HRD использованием стандартных методов миграции Google .
Шаги
- Добавьте адреса для вашего приложения
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()
- Затем добавить миграции кода
####
# 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
####
- Изменение
MODELSпеременная добавления модели и поля капля информации, которую необходимо обновить - Замените
myoldappадреса с фактическим URL старых приложений - Синхронизация кода и приложений
- Откройте браузер и укажите в нем
http://mynewapp.appspot.com/mig/__migrateblobs. Конечно,mynewappдолжны быть заменены на ваши фактические HRD имя приложения. Это позволит загрузить капли от старых приложений с новой. Потому что есть ожидания ограничение необходимо называть его много раз. ВНИМАНИЕ: Вы должны сделать только один запрос на время, не вызывать его из двух браузеров / вкладок в то же время, или вы запутались миграции баз данных!. Таким образом, этот шаг должен быть тщательно выполнен, одна просьба во время и дождаться ее завершения до вызова следующей. Повторите это, пока не появится сообщениеNothing to migrate!. Этот шаг может быть очень медленным в зависимости от того, сколько файлов находится в blobstore и их размер, это может занять много времени, чтобы все файлы загружены. Будьте терпеливы! - После того как все файлы копируются в новое приложение, указать браузеру
http://mynewapp.appspot.com/mig/__migratereferences. Должно быть быстрым, и она должна быть выполнена только один раз. Однако не вредит называют его более одного раза. - Последний шаг, удалите код миграции и адреса и синхронизировать код снова. Вы также можете удалить
mig_BlobMigбазе данных.
Хорошо миграции.
