Non conosco knex in dettaglio e da una rapida ricerca, knex attualmente non supporta l'uso di "limit" sulle istruzioni di aggiornamento, quindi solo una descrizione dell'approccio generale.
Per prima cosa esegui un aggiornamento per la riga che corrisponde ai criteri, quindi seleziona la riga aggiornata.
Quindi, prima esegui un'operazione di aggiornamento che assegni l'ID utente corrente alla prima riga non elaborata a cui non è stato assegnato alcun utente o è già stato assegnato lo stesso utente:
update rows
set assignedTo = user.id
where assignedTo=0 or assignedTo=user.id
order by createdAt asc
limit 1
Penso che possa funzionare in questo modo con knex usando una query grezza ma non l'ho provato:
await knex.raw('update rows set assignedTo = :userid where assignedTo=0 or assignedTo= :userid order by createdAt asc limit 1', {userid: user.id})
Questo cercherà la prima riga (creata in precedenza) che non è assegnata o già assegnata allo stesso utente e quindi assegnerà quell'utente. Questo accade in una volta sola.
È quindi possibile cercare la riga assegnata all'utente:
const notProcessed = await knex('rows')
.select('*'')
.whereRaw(`status='Not-Processed' and assignedTo=${user.id}`)
.orderByRaw('createdAt asc')
.first();
Nota come ora cerchiamo esplicitamente solo una riga già assegnata all'utente.
Combinato
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();
Ovviamente non hai bisogno del select se non vuoi lavorare subito con la riga.
Il problema è che quando vengono gestite più richieste contemporaneamente, devi immaginare più istanze del codice in esecuzione contemporaneamente in parallelo. Quindi, con il tuo codice originale, due richieste potrebbero eseguire la tua selezione contemporaneamente prima che una di esse esegua un aggiornamento. Quindi, entrambi avranno la stessa riga restituita.
Aggiornando immediatamente la riga all'interno dell'istruzione, anche quando due istruzioni vengono eseguite in parallelo, il database si assicurerà che non vedano la stessa riga.
Un approccio alternativo per una soluzione sarebbe usare un mutex (come ad esempio async-mutex ) attorno al tuo codice originale per assicurarti che l'operazione di selezione e aggiornamento originale sia atomica (succede in una volta sola), ma questo molto probabilmente aumenterà il tempo di risposta della tua applicazione perché in alcune situazioni un'operazione di gestione delle richieste dovrà attendere un'altra uno per continuare.