7

Ngay bây giờ tôi sử dụng remote_api và appcfg.py download_data để chụp nhanh cơ sở dữ liệu của mình mỗi tối. Phải mất một thời gian dài (6 giờ) và rất tốn kém. Nếu không có bản sao lưu dựa trên thay đổi của riêng tôi (tôi sẽ quá sợ hãi để làm điều gì đó như vậy), lựa chọn nào tốt nhất để đảm bảo dữ liệu của tôi an toàn khỏi thất bại?Các chiến lược được khuyến nghị để sao lưu kho dữ liệu appengine

PS: Tôi nhận ra rằng dữ liệu của Google có lẽ an toàn hơn tôi nhiều. Nhưng nếu một ngày nào đó tôi vô tình viết một chương trình xóa tất cả?

+0

Về câu hỏi cuối cùng của bạn, gắn dấu sao lỗi này: [776] (http://code.google.com/p/googleappengine/issues/detail?id=776) –

Trả lời

3

Tôi nghĩ bạn đã xác định được tất cả các lựa chọn của mình.

  1. Tin tưởng Google không mất dữ liệu của bạn và hy vọng bạn không vô tình yêu cầu họ hủy dữ liệu.
  2. Thực hiện sao lưu đầy đủ với download_data, có thể ít thường xuyên hơn một lần mỗi đêm nếu nó cực kỳ tốn kém.
  3. Cuộn giải pháp sao lưu gia tăng của riêng bạn.

Tùy chọn 3 thực sự là một ý tưởng thú vị. Bạn sẽ cần một dấu thời gian sửa đổi trên tất cả các thực thể, và bạn sẽ không bắt được các thực thể bị xóa, nhưng nếu không nó sẽ rất khả thi với remote_api và các con trỏ.

Sửa:

Dưới đây là một downloader gia tăng đơn giản để sử dụng với remote_api. Một lần nữa, hãy cẩn thận rằng nó sẽ không nhận thấy các thực thể bị xóa, và nó giả định tất cả các thực thể lưu trữ thời gian sửa đổi cuối cùng trong một thuộc tính có tên là updated_at. Sử dụng nó theo nguy hiểm của riêng bạn.

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