loco-rs / loco

🚂 🦀 The one-person framework for Rust for side-projects and startups
https://loco.rs
Apache License 2.0
5.45k stars 235 forks source link

SQLite worker queue implementation #969

Closed isaacdonaldson closed 2 weeks ago

isaacdonaldson commented 2 weeks ago

This is an implementation of an SQLite background worker queue.

Since SQLite does not have FOR UPDATE SKIP LOCKED and sqlx does not support BEGIN IMMEDIATE transactions (from what I could tell), I opted to use another table aquire_queue_write_lock as concurrency control.

When reading for dequeue, it will attempt to update a field in the table, on success it will turn the transaction into a write transaction (from the starting point of read transaction). This will prevent the other readers from reading a queued task until the current one is done selecting one. On failure it will not select a task as there is another transaction selecting one, making sure that no task is selected twice.

jondot commented 2 weeks ago

wow i was hoping that someone with sqlite expertise come in and implement this one! thank you!! i'm not an expert but I've just spent some time reading about other sqlite implementations, and it looks like people say that sqlite locks the entire DB on writes. would that obsolete the locking table/issue?

isaacdonaldson commented 2 weeks ago

It does lock on writes, but the dequeue method is a read transaction until it finds a task, then afterwords when claiming it, it gets upgraded to a write transaction. The write into the lock_queue will upgrade the transaction to a write queue before it fetches a task, so it will be locked for the read and remove the chance a task is claimed twice.

jondot commented 2 weeks ago

Makes sense. Fantastic contribution ❤️