Проведение транзакции без использования базы данных с высокой репликацией - PullRequest
0 голосов
/ 04 декабря 2011

У меня есть приложение, которое выполняет следующую задачу.

  1. Аутентифицировать пользователя по его электронной почте и паролю.
  2. Сохраните загруженный файл в BlobStore.
  3. Проверьте информацию о пользователе из хранилища данных, чтобы увидеть, существует ли старый двоичный объект, связанный с этим пользователем. Если да, удалите старый BLOB-объект из BlobStore.
  4. Обновить DataStore, чтобы связать новый BLOB-объект в BlobStore с этим пользователем.

Я пытаюсь выполнить шаги 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()

Ответы [ 2 ]

1 голос
/ 06 декабря 2011

Хранилище данных HR и хранилище данных M / S теперь имеют одинаковую цену при новом биллинге.На самом деле нет причин не использовать хранилище данных HR.

get_blob_key должен выполнить запрос, чтобы найти BLOB-объект, соответствующий имени файла.Выполняйте всю работу вплоть до транзакции и выполняйте только обновления внутри транзакции.Обратите внимание, однако, что вы ничего не можете сделать, что сделает весь этот процесс транзакционным, потому что сами обновления в онлайн-магазине не являются.

1 голос
/ 05 декабря 2011

Мне кажется, я вижу то, что вы пытаетесь выполнить с помощью транзакции: либо создайте как объект хранилища BLOB-объектов, так и объект хранилища данных (Human), либо не создавайте ни один из них. Но ваш подход вызывает у вас несколько проблем, одна из которых заключается в том, что вы не можете выполнять запросы не-предков внутри транзакций. Вы видите это, когда вы делаете get_blob_key, но вы также получите этот запрос для Human с. (Первая ошибка скрывает вторую.) И затем возникает проблема создания совершенно нового Human вместо обновления существующего, которое останется с ключом к удаленному BLOB-объекту.

Самый простой путь вперед - отказаться от транзакции. Сохраните блоб, затем определите, знаете ли вы об этом Человеке. Если да, удалите старый блоб и обновите Человека. Если нет, создайте нового Человека с недавно сохраненным ключом BLOB-объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...