2011-12-04 19 views
0

我有一個應用程序執行以下任務。交易沒有使用高複製數據庫

  1. 通過他的電子郵件和密碼驗證用戶。
  2. 將上傳的文件保存在BlobStore中。
  3. 檢查DataStore中的用戶信息,以查看是否存在與此用戶關聯的舊Blob。如果是,請從BlobStore中刪除舊的Blob。
  4. 更新DataStore,將BlobStore中的新Blob與此用戶相關聯。

我嘗試在事務中執行第2,3,4步。

db.run_in_transaction(self.upload, email, checksum, version, content) 

但是,正如預期的那樣,由於我訪問的實體超過1個,我得到以下錯誤。

BadRequestError: can't operate on multiple entity groups in a single transaction. 

我不太高興。至於,如果它不能跨多個表(實體)執行原子操作,那麼事務的用法是什麼?

我強迫使用高複製數據庫。 (這將花費我,開票期限)

db.run_in_transaction_options(xg_on, self.upload, email, checksum, version, content) 

同樣,我得到以下錯誤:

BadRequestError: Only ancestor queries are allowed inside transactions. 

這發生在行:

blob_key = files.blobstore.get_blob_key(file_name) 

我的問題是: -

  1. 是否有任何方法讓我們跨多個「表」執行事務,就像我通過PostgresSQL所能做的那樣,而不需要使用高複製數據存儲?只要成本受到關注,主/從數據存儲就會讓我滿意。
  2. 我能做些什麼來將blob_key = files.blobstore.get_blob_key(file_name)變成祖先查詢?這樣它會在事務內部工作嗎?或者,簡而言之,我如何才能使def upload在交易中起作用?

我有完整的代碼如下:


import urllib 
import logging 
import model 
import zlib 
from google.appengine.api import urlfetch 
from google.appengine.ext import webapp 
from google.appengine.ext.webapp.util import run_wsgi_app 
from google.appengine.api import files 
from google.appengine.ext import db 
from google.appengine.ext import blobstore 

xg_on = db.create_transaction_options(xg=True) 


class Upload(webapp.RequestHandler): 
    def post(self): 
     email = self.request.get('Email') 
     password = self.request.get('Passwd') 
     checksum = int(self.request.get('Checksum')) 
     version = int(self.request.get('Version')) 
     logintoken = self.request.get('logintoken') 
     logincaptcha = self.request.get('logincaptcha') 
     content = self.request.get('file') 

     if version == -1: 
      self.response.out.write('ERROR [invalid parameter(s)]') 
      return 

     # Ensure the uploaded content is valid. 
     if content is None or not content: 
      self.response.out.write('ERROR [no file is uploaded]') 
      return 

     # Authentication. 
     headers = {"Content-type": "application/x-www-form-urlencoded"} 
     if logintoken and logincaptcha: 
      form_data = urllib.urlencode({ 
       'accountType': 'HOSTED_OR_GOOGLE', 
       'Email': email, 
       'Passwd': password, 
       'service': 'mail', 
       'source': 'JStock-1.05b', 
       'logintoken': logintoken, 
       'logincaptcha': logincaptcha 
      }) 
     else: 
      form_data = urllib.urlencode({ 
       'accountType': 'HOSTED_OR_GOOGLE', 
       'Email': email, 
       'Passwd': password, 
       'service': 'mail', 
       'source': 'JStock-1.05b' 
      }) 
     result = urlfetch.fetch(url='https://www.google.com/accounts/ClientLogin', payload=form_data, method=urlfetch.POST, headers={'Content-Type': 'application/x-www-form-urlencoded'}) 
     self.response.set_status(result.status_code) 
     if result.status_code != 200: 
      # Fail. Either incorrect password or captcha information required. 
      self.response.out.write(result.content) 
      return 

     # OK! This is a valid user. Let's proceed with checksum verification. 
     ##if checksum != zlib.adler32(content): 
     ## self.response.out.write('ERROR [fail in checksum]') 
     ## return    

     #db.run_in_transaction(self.upload, email, checksum, version, content) 
     db.run_in_transaction_options(xg_on, self.upload, email, checksum, version, content) 
     #self.upload(email, checksum, version, content) 


    def upload(self, email, checksum, version, content): 
     # Create the file 
     file_name = files.blobstore.create(mime_type='application/octet-stream', _blobinfo_uploaded_filename=email) 

     # Open the file and write to it 
     with files.open(file_name, 'a') as f: 
      f.write(content) 

     # Finalize the file. Do this before attempting to read it. 
     files.finalize(file_name) 

     # Get the file's blob key 
     blob_key = files.blobstore.get_blob_key(file_name) 

     # Remove previous blob referenced by this human. 
     query = model.Human.all() 
     query.filter('email =', email) 
     for q in query: 
      blobstore.delete(q.content.key()) 

     human = model.Human(key_name=email, email=email, checksum=checksum, version=version, content=blob_key) 
     human.put() 


application = webapp.WSGIApplication([ 
    ('/upload.py', Upload) 
], debug=True) 


def main(): 
    run_wsgi_app(application) 


if __name__ == '__main__': 
    main() 

回答

1

現在,HR數據存儲和M/S數據存儲在新計費下的價格相同。真的沒有理由不使用HR數據存儲。

get_blob_key必須執行查詢以查找與文件名相對應的blob。完成交易之外的所有工作,並僅執行交易內的更新。但請注意,您所做的任何事情都不會使整個流程成爲事務性的 - 因爲blobstore更新本身並非如此。

1

我想我看到你正在試圖通過交易來完成的:要麼同時創建Blob存儲對象和數據存儲對象(人類),或者不創建它們。但是你的方法會導致你幾個問題,其中之一是你不能在事務內部進行非祖先查詢。當您執行get_blob_key時,您會看到,但您也會收到Human的查詢。 (第一個錯誤隱藏了第二個錯誤。)然後就是創建一個全新的Human,而不是更新現有的一個,而現有的一個將保留一個鍵到一個已刪除的blob。

最簡單的方法是免除交易。存儲blob,然後確定你是否知道這個人。如果是,請刪除舊的blob,並更新Human。如果不是,則使用新存儲的blob密鑰創建一個新的Human。