7

Ahora uso remote_api y appcfg.py download_data para tomar una instantánea de mi base de datos todas las noches. Lleva mucho tiempo (6 horas) y es costoso. Sin lanzar mi propia copia de seguridad basada en cambios (me daría demasiado miedo hacer algo como eso), ¿cuál es la mejor opción para asegurarme de que mi información esté a salvo de fallas?Estrategias recomendadas para realizar copias de seguridad del almacén de datos de appengine

PD: Reconozco que los datos de Google son probablemente mucho más seguros que los míos. ¿Pero qué pasa si un día accidentalmente escribo un programa que lo elimina todo?

+0

Acerca de su última pregunta, marque este error: [776] (http://code.google.com/p/googleappengine/issues/detail?id=776) –

Respuesta

3

Creo que ya ha identificado prácticamente todas sus opciones.

  1. Confíe en Google para no perder sus datos, y espero que no les indique accidentalmente que lo destruyan.
  2. Realice copias de seguridad completas con download_data, tal vez con menos frecuencia que una vez por noche si es prohibitivamente costoso.
  3. Despliegue su propia solución de copia de seguridad incremental.

La opción 3 es realmente una idea interesante. Necesitaría una marca de tiempo de modificación en todas las entidades, y no captaría las entidades eliminadas, pero de lo contrario es muy factible con remote_api y los cursores.

Editar:

Aquí está un simple descargador incrementales para su uso con remote_api. Nuevamente, las advertencias son que no notará las entidades eliminadas, y asume que todas las entidades almacenan la última modificación en una propiedad llamada updated_at. Úselo bajo su propio riesgo.

import os 
import hashlib 
import gzip 
from google.appengine.api import app_identity 
from google.appengine.ext.db.metadata import Kind 
from google.appengine.api.datastore import Query 
from google.appengine.datastore.datastore_query import Cursor 

INDEX = 'updated_at' 
BATCH = 50 
DEPTH = 3 

path = ['backups', app_identity.get_application_id()] 
for kind in Kind.all(): 
    kind = kind.kind_name 
    if kind.startswith('__'): 
    continue 
    while True: 
    print 'Fetching %d %s entities' % (BATCH, kind) 
    path.extend([kind, 'cursor.txt']) 
    try: 
     cursor = open(os.path.join(*path)).read() 
     cursor = Cursor.from_websafe_string(cursor) 
    except IOError: 
     cursor = None 
    path.pop() 
    query = Query(kind, cursor=cursor) 
    query.Order(INDEX) 
    entities = query.Get(BATCH) 
    for entity in entities: 
     hash = hashlib.sha1(str(entity.key())).hexdigest() 
     for i in range(DEPTH): 
     path.append(hash[i]) 
     try: 
     os.makedirs(os.path.join(*path)) 
     except OSError: 
     pass 
     path.append('%s.xml.gz' % entity.key()) 
     print 'Writing', os.path.join(*path) 
     file = gzip.open(os.path.join(*path), 'wb') 
     file.write(entity.ToXml()) 
     file.close() 
     path = path[:-1-DEPTH] 
    if entities: 
     path.append('cursor.txt') 
     file = open(os.path.join(*path), 'w') 
     file.write(query.GetCursor().to_websafe_string()) 
     file.close() 
     path.pop() 
    path.pop() 
    if len(entities) < BATCH: 
     break 
Cuestiones relacionadas