0

バックエンドとして RESTful フレームワーク (Flask-Restless 0.17.0 with Flask-SQLAlchemy) を使用しています。そしてAngularJSをフロントエンドとして。

たとえば、単一のテーブルのバージョン列 (またはデータのタイムスタンプまたはチェックサム) を使用して同時実行性を処理できることはわかっています。

私が今それを処理する方法は次のようなものです:

すべての SQLAlchemy モデルは CommonColumns から継承しています:

class CommonColumns(db.Model):
    __abstract__ = True
    id = db.Column(db.Integer, primary_key=True)    
    aangemaakt = db.Column(db.DateTime)
    gewijzigd = db.Column(db.DateTime)
    etag = db.Column(db.String(40))

def commoncolumns_on_before_insert(mapper, connection, target):
    """ Set time created and generate ETag on before_insert event. """
    # Set time created.
    target.aangemaakt = datetime.now()
    # Generate ETag based on SHA1-hash of time created.
    target.etag = hashlib.sha1(str(target.aangemaakt)).hexdigest()

def commoncolumns_on_before_update(mapper, connection, target):
    """ Set time updated and generate ETag on before_update event. """
    # Set time updated.
    target.gewijzigd = datetime.now()
    # Generate ETag based on SHA1-hash of time updated.
    target.etag = hashlib.sha1(str(target.gewijzigd)).hexdigest()

event.listen(CommonColumns, 'before_insert', commoncolumns_on_before_insert, propagate = True)
event.listen(CommonColumns, 'before_update', commoncolumns_on_before_update, propagate = True)

各リクエストの後、いくつかのコードが etag-column を探し、そのヘッダーを作成します。

@app.after_request
def add_etag_header(response):
    """ Add etag-header contained in 'etag'-field inside returned JSON-object.
    If no JSON returned, it wil silently ignore the exception and return the
    response it would have returned anyway.
    """
    try:
        # Parse JSON.
        jsonObject = json.loads(response.get_data())
        # Get etag field (if one)
        etag = jsonObject.get('etag', None)
        if etag != None:
             # Return ETag as a header (for client, e.g. Restangular).
            response.headers['ETag'] = etag
    except Exception:
        # Some unexpected exception occurred. Ignore for now.
        pass
    return response    

次に、API リソースごとに、この関数を呼び出すプリプロセッサを使用します。

def abort_on_etag_collision(model, instance_id):
    """ Abort PUT/DELETE-operation for model (with ID=instance_id) when there is a 'mid-air-collision'
    (when client-side and server-side ETags do not match). """
    # Store client-side ETag for comparison.
    hdr_ifmatch_etag = request.headers.get('If-Match', None)
    # Get server-side ETag from model for comparison.
    current_etag = db.session.query(model).get(instance_id).etag
    if current_etag == hdr_ifmatch_etag:
        # Current ETag matches client-specified ETag, so let request through
        pass
    else:
        # Current ETag is different from client-specified ETag, so return a 412 Precondition Failed!
        # Also called a 'mid-air-collision'...
        raise ProcessingException(description='Precondition Failed', code=412)

「abort_on_etag_collision」関数も競合状態に対して脆弱だと思いますか?

しかし、次に、関連するテーブルが複数ある場合はどうでしょう。これらのテーブルには、親リソースを介してアクセスできますが、子リソースを介してアクセスすることもできます。これに対処するための最良または最も柔軟な方法が何であるかを理解するのに苦労しています.

SQLALchemy のバージョン管理機能を使用したい: http://docs.sqlalchemy.org/en/rel_1_0/orm/versioning.html

しかし、SQLAlchemy はこれを自動的に行わず、Flask-Restless を使用する可能性が見当たらないため、SQL を変更してバージョンの WHERE チェックを自分で含めることができるようにする必要があります。

どんな助けでも大歓迎です。

4

1 に答える 1

0

ここで私の質問に対する答えを見つけました: https://stackoverflow.com/a/14690307/694400

要約: これには (タイムスタンプを使用する代わりに) チェックサムを使用する必要があります。

于 2016-05-12T21:25:00.300 に答える