No conozco knex en detalle y, a partir de una búsqueda rápida, knex actualmente no admite el uso de "límite" en declaraciones de actualización, por lo que solo es una descripción del enfoque general.
Primero haga una actualización para la fila que coincida con los criterios, luego seleccione esa fila actualizada.
Por lo tanto, primero realice una operación de actualización que asigne el ID de usuario actual a la primera fila sin procesar que no tenga ningún usuario asignado o que ya tenga asignado el mismo usuario:
update rows
set assignedTo = user.id
where assignedTo=0 or assignedTo=user.id
order by createdAt asc
limit 1
Creo que puede funcionar así con knex usando una consulta sin procesar, pero no lo he probado:
await knex.raw('update rows set assignedTo = :userid where assignedTo=0 or assignedTo= :userid order by createdAt asc limit 1', {userid: user.id})
Esto buscará la primera fila (la más antigua creada en) que no está asignada o ya está asignada al mismo usuario y luego asigna ese usuario. Esto sucede de una sola vez.
A continuación, puede buscar la fila asignada al usuario:
const notProcessed = await knex('rows')
.select('*'')
.whereRaw(`status='Not-Processed' and assignedTo=${user.id}`)
.orderByRaw('createdAt asc')
.first();
Observe cómo ahora buscamos explícitamente solo una fila ya asignada al usuario.
Combinado
await knex.raw('update rows set assignedTo = :userid where assignedTo=0 or assignedTo= :userid order by createdAt asc limit 1', {userid: user.id})
const notProcessed = await knex('rows')
.select('*'')
.whereRaw(`status='Not-Processed' and assignedTo=${user.id}`)
.orderByRaw('createdAt asc')
.first();
Obviamente, no necesita seleccionar si no desea trabajar con la fila de inmediato.
El problema es que cuando se manejan varias solicitudes al mismo tiempo, debe imaginar varias instancias del código ejecutándose al mismo tiempo en paralelo. Entonces, con su código original, dos solicitudes podrían hacer su selección al mismo tiempo antes de que cualquiera de ellas realice una actualización. Entonces, a ambos se les devolverá la misma fila.
Al actualizar inmediatamente la fila dentro de la declaración, incluso cuando dos declaraciones se ejecutan en paralelo, la base de datos se asegurará de que no vean la misma fila.
Un enfoque alternativo para una solución sería usar un mutex (como, por ejemplo, async-mutex ) alrededor de su código original para asegurarse de que su operación original de selección y actualización sea atómica (ocurre de una sola vez), pero esto probablemente aumentará el tiempo de respuesta de su aplicación porque en algunas situaciones una operación de manejo de solicitud tendrá que esperar a otra uno para continuar.