Я использую RESTful framework (Flask-Restless 0.17.0 с Flask-SQLAlchemy) в качестве бэкэнд. И AngularJS как интерфейс.Как справиться с оптимистичным параллелизмом для ресурсов REST?
Я знаю, что можно обрабатывать параллелизм, используя, например, столбец версии (или временную метку или контрольную сумму данных) для одной таблицы.
Путь я сейчас обращения с ним, как это:
Все 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 столбца и создает заголовок для этого:
@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
Но тогда я должен быть в состоянии изменить SQL включить WHERE-проверку версии (ы) себя как SQLAlchemy не сделайте это автоматически, и я не вижу возможности для этого использовать Flask-Restless.
Любая помощь приветствуется.
Привет, когда вы понимаете, что нашли ответ по другому вопросу в переполнении стека, часто лучше всего закрыть свой исходный вопрос в качестве дубликата этого. Таким образом, дублирующие вопросы служат указателями на центральный Q/A, а не фрагментируют ответы на вопросы. – Serlite
@Serlite Кажется логичным. Я попробую! – Wieger