Imagine que tiene una tabla simple con elementos de trabajo:
|ID |OWNER|... +---+-----+--- |123| |... |456| |... |789| |...Queremos proporcionar una API http para obtener el siguiente elemento de trabajo que aún no tiene propietario.
Usamos PostgreSQL.
Accedemos a la tabla con Django-ORM.
Supongo que hay varias condiciones de carrera si muchos usuarios acceden simultáneamente a la API.
¿Cómo puedo asegurarme con las herramientas proporcionadas (PostgreSQL, Django) de que se resuelven todas las condiciones de carrera (es una falla importante si un elemento de trabajo se entrega a dos o más usuarios)?
Con Django 1.11, select_for_update comenzó a admitir skip_locked . Esto significa que puede ahorrar en llamadas save() ya que no tiene que asignarlo a un propietario de inmediato.
Por ejemplo, construyendo sobre la respuesta de @user73657:
with transaction.atomic(): work_item = WorkItem.objects.select_for_update().filter(owner__isnull=True).first() work_item.owner = request.user work_item.save(update_fields=['owner']) # process work_itemtu puedes hacer:
with transaction.atomic(): work_item = WorkItem.objects.select_for_update(skip_locked=True).filter(owner__isnull=True).first() work_item.owner = request.user # process work_item, edit other fields work_item.save() Con skip_locked=True , la transacción omite la fila bloqueada y, por lo tanto, no bloquea. Como beneficio adicional, solo tendrá que guardar en la base de datos una vez .
Con select_for_update :
with transaction.atomic(): work_item = WorkItem.objects.select_for_update().filter(owner__isnull=True).first() work_item.owner = request.user work_item.save(update_fields=['owner']) # process work_itemhttps://docs.djangoproject.com/en/1.11/ref/models/querysets/#select-for-update
select_for_update se asegurará de que solo una conexión pueda actualizar las filas coincidentes hasta que finalice la transacción.