Je ne connais pas knex en détail et d'après une recherche rapide, knex ne prend actuellement pas en charge l'utilisation de "limite" sur les instructions de mise à jour, donc juste une description de l'approche générale.
Effectuez d'abord une mise à jour pour la ligne correspondant aux critères, puis sélectionnez cette ligne mise à jour.
Donc, faites d'abord une opération de mise à jour qui attribue l'ID utilisateur actuel à la première ligne non traitée qui n'a pas d'utilisateur attribué ou qui a déjà le même utilisateur attribué :
update rows
set assignedTo = user.id
where assignedTo=0 or assignedTo=user.id
order by createdAt asc
limit 1
Je pense que cela peut fonctionner comme ceci avec knex en utilisant une requête brute, mais je n'ai pas essayé :
await knex.raw('update rows set assignedTo = :userid where assignedTo=0 or assignedTo= :userid order by createdAt asc limit 1', {userid: user.id})
Cela recherchera la première ligne (la plus ancienne créée à) non attribuée ou déjà attribuée au même utilisateur, puis attribuera cet utilisateur. Cela se produit en une seule fois.
Vous pouvez ensuite rechercher la ligne attribuée à l'utilisateur :
const notProcessed = await knex('rows')
.select('*'')
.whereRaw(`status='Not-Processed' and assignedTo=${user.id}`)
.orderByRaw('createdAt asc')
.first();
Remarquez comment nous ne recherchons maintenant explicitement qu'une ligne déjà attribuée à l'utilisateur.
Combiné
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();
Évidemment, vous n'avez pas besoin de sélectionner si vous ne voulez pas travailler avec la ligne immédiatement.
Le problème est que lorsque plusieurs requêtes sont traitées en même temps, vous devez imaginer plusieurs instances du code s'exécutant en même temps en parallèle. Ainsi, avec votre code d'origine, deux requêtes pourraient faire votre sélection en même temps avant que l'une d'entre elles ne fasse une mise à jour. Ainsi, les deux auront la même ligne retournée.
En mettant à jour immédiatement la ligne dans l'instruction, même lorsque deux instructions s'exécutent en parallèle, la base de données s'assurera qu'elles ne voient pas la même ligne.
Une approche alternative pour une solution serait d'utiliser un mutex (comme par exemple async-mutex ) autour de votre code d'origine pour vous assurer que votre opération de sélection et de mise à jour d'origine est atomique (se produit en une seule fois), mais cela augmentera très probablement le temps de réponse de votre application car dans certaines situations, une opération de traitement des demandes devra attendre une autre un pour continuer.