2017-12-22 16 views
3

私は、竜巻非同期フレームワークで書かれたpythonアプリを持っています。 HTTPリクエストが到着すると、このメソッドが呼び出されます。データベースに竜巻リクエストをアトミックにする方法

@classmethod 
def my_method(cls, my_arg1): 

    # Do some Database Transaction #1 
    x = get_val_from_db_table1(id=1, 'x') 
    y = get_val_from_db_table2(id=7, 'y') 
    x += x + (2 * y) 

    # Do some Database Transaction #2 
    set_val_in_db_table1(id=1, 'x', x) 

    return True 

3つのデータベース操作は相互に関連しています。そして、これは同時アプリケーションです。そのようなHTTPコールが複数同時に発生し、同じDBに到達する可能性があります。

データの完全性のために、このメソッドの3つのデータベース操作は、別のプロセスがそれらの間のデータベース行を読み書きすることなく呼び出されることが重要です。

このメソッドにデータベースのアトミック性があることを確認するにはどうすればよいですか?竜巻にはこれのデコレータがありますか?

答えて

2

同期データベースアクセス

あなたはあなたのデータベースにアクセスする方法を述べていない:

ここであなたが始めるために、関連するコードです。おそらく、get_val_from_db_table1に同期DBアクセスがあり、友人(例えば、pymysql)とmy_methodがブロックしている(IOループに制御を戻していない)場合は、サーバーをブロックする(サーバーのパフォーマンスと応答性に影響します)クライアントを効果的にシリアライズし、一度に実行できるのはmy_methodだけです。したがって、データの一貫性の点では何もする必要はありませんが、一般的にはそれは悪い設計です。 @ xyresのソリューションを短期間で解決することができます(Tornadoの機能のほとんどがisn't thread-safeなので、スレッドセーフであることを念頭に置いてください)。

非同期データベースアクセス

あなたはget_val_from_db_table1や友人(例えばtornado-mysqlと)非同期DBアクセスを持っているなら、あなたはtornado.locks.Lockを使用することができます。ここでは例です:上記は通常のシングルプロセストルネードアプリケーションについて言われていること

from tornado import web, gen, locks, ioloop 


_lock = locks.Lock() 

def synchronised(coro): 
    async def wrapper(*args, **kwargs): 
     async with _lock: 
      return await coro(*args, **kwargs) 

    return wrapper 


class MainHandler(web.RequestHandler): 

    async def get(self): 
     result = await self.my_method('foo') 
     self.write(result) 

    @classmethod 
    @synchronised 
    async def my_method(cls, arg): 
     # db access 
     await gen.sleep(0.5) 
     return 'data set for {}'.format(arg) 


if __name__ == '__main__': 
    app = web.Application([('/', MainHandler)]) 
    app.listen(8080) 
    ioloop.IOLoop.current().start() 

注意。 tornado.process.fork_processesを使用している場合は、multiprocessing.Lockとしか行けません。

2

これらの3つのDB操作をすぐ後で実行するため、関数my_methodは非同期である必要があります。

しかし、これはまた、my_methodがサーバーをブロックすることを意味します。あなたは間違いなくそれを望んでいます。私が考えることの1つの方法は、この関数を別のスレッドで実行することです。これによりサーバーはブロックされず、操作が実行されている間も新しい要求を受け入れ続けます。そして、非同期であるため、dbのアトミック性が保証されます。

import concurrent.futures 

executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) 
# Don't set `max_workers` more than 1, because then multiple 
# threads will be able to perform db operations 

class MyHandler(...): 
    @gen.coroutine 
    def get(self): 

     yield executor.submit(MyHandler.my_method, my_arg1) 
     # above, `yield` is used to wait for 
     # db operations to finish 
     # if you don't want to wait and return 
     # a response immediately remove the 
     # `yield` keyword 

     self.write('Done') 

    @classmethod 
    def my_method(cls, my_arg1): 
     # do db stuff ... 
     return True 
関連する問題