knexの詳細はわかりませんが、クイック検索から、knexは現在、更新ステートメントでの「制限」の使用をサポートしていないため、一般的なアプローチの説明にすぎません。
まず、条件に一致する行を更新してから、更新された行を選択します。
したがって、最初に、現在のユーザーIDを、ユーザーが割り当てられていないか、同じユーザーがすでに割り当てられている最初の未処理の行に割り当てる更新操作を実行します。
update rows
set assignedTo = user.id
where assignedTo=0 or assignedTo=user.id
order by createdAt asc
limit 1
生のクエリを使用したknexでこのように機能すると思いますが、試したことはありません:
await knex.raw('update rows set assignedTo = :userid where assignedTo=0 or assignedTo= :userid order by createdAt asc limit 1', {userid: user.id})
これにより、割り当てられていないか、同じユーザーにすでに割り当てられている最初の(最も早いcreatedAt)行が検索され、そのユーザーが割り当てられます。これは一度に起こります。
次に、ユーザーに割り当てられた行を検索できます:
const notProcessed = await knex('rows')
.select('*'')
.whereRaw(`status='Not-Processed' and assignedTo=${user.id}`)
.orderByRaw('createdAt asc')
.first();
ユーザーにすでに割り当てられている行のみを明示的に検索する方法に注目してください。
組み合わせ
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();
明らかに、行をすぐに操作したくない場合は、選択する必要はありません。
問題は、複数のリクエストが同時に処理される場合、コードの複数のインスタンスが同時に並行して実行されることを想像する必要があることです。したがって、元のコードを使用すると、2つのリクエストが、いずれかが更新を行う前に、同時に選択を行うことができます。したがって、両方とも同じ行が返されます。
ステートメント内の行をすぐに更新することにより、2つのステートメントが並行して実行されている場合でも、データベースはそれらに同じ行が表示されないようにします。
ソリューションの代替アプローチは、ミューテックスを使用することです(例: async-mutex > )元のコードを囲んで、元の選択と更新の操作がアトミックであることを確認します(一度に発生します)が、状況によっては、あるリクエスト処理操作が別のリクエスト処理操作を待機する必要があるため、アプリケーションの応答時間が長くなる可能性があります。続行します。