Database Row Locking and State Machine Design Behind Three Computers Racing to Claim Tasks
When three computers run the image generation script simultaneously, what you are facing is not a queuing system but a task-claiming mechanism based on database row locking. Each computer is an independent worker client, and they all do exactly the same thing: repeatedly execute a single SQL update statement to try to claim a pending task from the dbvideo_arcpictask table. The status field of this table takes only three values: 0 means pending, 1 means processing, and 2 means completed. Each record also contains a worker_id field that records which client claimed the task. The key to concurrency control lies in the update statement carrying the status=0 filter condition. When one computer executes `update dbvideo_arcpictask set status=1, worker_id='PC-1' where status=0 limit 1`, the database row lock guarantees that the same row can only be successfully updated by one connection at any given moment. The identical update statements from the other two computers will return zero affected rows because the condition is no longer satisfied, causing them to naturally skip that row and continue searching for the next record where status is still 0. This process is atomic inside the database engine and requires no additional distributed locks or coordination servers. The entire flow only needs a short-lived transaction wrapping this update and the subsequent select. Once the transaction commits, other computers can see that the task has been marked as processing. After the transaction completes, the client queries all tasks with status=1 assigned to it from the arcpictask table by its own worker_id and begins calling the external image generation interface one by one. The database itself acts as the central scheduler for task allocation, and it naturally achieves load balancing through row locks. In the actual operation logs of the "The First Avenue" project, the image generation phase was scheduled in a centralized evening session. The three computers ran simultaneously from 8 PM, processing all pending tasks in the review, and finished by 6 AM the next morning. A total of 1,029 shots required first-frame and last-frame image generation. Since each shot generates two images, 2,058 images needed to be produced. The number of images completed by each computer was not exactly equal: PC-1 completed 712, PC-2 completed 658, and PC-3 completed 688. The differences mainly came from each machine's per-image generation speed being affected by VRAM and CPU frequency. In the end, all tasks reached status equal to 2, with no task forgotten or executed twice. One detail that is easily overlooked in concurrency control is how to handle task timeouts. Suppose PC-1 claims a task and then the network suddenly drops, or the image generation interface returns an error. In that case, the record will remain stuck in status=1. If no intervention occurs, it becomes a zombie task that will never be processed by another computer. To avoid this, Yuncao Academy sets a lastbeat field in the arcpictask table. Whenever a client starts processing a task, it periodically updates this field to the current timestamp, acting as a heartbeat signal. A background watchdog process scans all records with status=1 every five minutes. When it finds a record whose lastbeat has not been updated for more than ten minutes, indicating the worker may have gone offline, the watchdog executes an update statement to forcibly reset that record to status=0, clear worker_id, and increment a retry_count field. This allows other computers to claim the task again on their next scan. In actual production, disconnections did occur. For example, PC-2 entered sleep mode at 2 AM due to its power settings. The watchdog automatically released all sixteen unfinished tasks it held, and these tasks were smoothly picked up by PC-1 and PC-3 on their next polling cycle, with no missing shots. The most direct benefit of this database state machine-based task scheduling is eliminating the tedium of manual task assignment. With a manual approach, you would need to know which task should go to which device, estimate each device's processing speed, manually update records after task completion, and deal with failures and retries at any time. The entire process is heavy and error-prone, especially when the task count reaches the thousands. The row-lock-based claiming mechanism achieves the most sensible load balancing at almost zero cost, because all idle clients naturally scan from the top of the table downward, skipping rows currently being processed by other clients. So every non-idle device grabs as many tasks as possible. This essentially leverages the relational database's native row-level concurrency control capability rather than an additional framework. Since the table has few rows and simple indexes, each update and select operation takes milliseconds. Even triggering thousands of task claims on a high-performance database imposes no noticeable strain on the system. Another notable aspect of the state machine design is its guarantee of idempotency. Each task uses a unique scenecode as its primary key. After completing a task, the client re-checks whether the current task's status in the arcpictask table is 2. If the status is still 1, it means the update failed or the result was not committed. In that case, the client temporarily saves the generated image result to local cache and re-executes the status confirmation. In the ideal case, this check never triggers. But in actual operation, there were two instances where, due to network latency, the generated image files had already been written to disk but the status had not been updated. Re-running the task in those cases regenerated the images and overwrote the previous files, rather than creating new copies, thus avoiding any confusion in the database state. This also ensures the entire system can re-run any storyboard's task at any time without producing duplicate records. For errors during generation, such as external interface timeout or incorrect image dimensions, the client increments retry_count and rolls status back to 0, allowing the task to re-enter the queue and be claimed by another machine on the next round. If a task fails more than five consecutive times, the system marks it as failed, leaves an alert log row in the database, but does not block the processing of other tasks. In the end, this lightweight scheduling showed extremely high stability in the project's final statistics. Over the two-week production period, the total number of image generation tasks was 2,058, all completed autonomously by the three computers. Not a single task was duplicated or missed due to concurrency competition. No message queue was introduced, and no Redis lock was configured—only row locks and status fields in a stable relational database. The essence of this design is migrating the complexity of task scheduling from the code layer to the data layer, replacing the clumsy approach of hand-writing scheduling logic with the database's native concurrency isolation capability. If you are planning to build your own multi-machine parallel AI content pipeline, you do not need to rush into introducing a distributed task framework. First define your task table, set up the status, worker_id, and lastbeat fields, and write a conditional update statement for task claiming. You will find that it already covers the vast majority of scenarios that do not require second-level scheduling precision, and it runs reliably at an extremely low implementation cost. For a film that aims to be completed in eleven days, this approach—letting three computers claim work on their own, execute what they grab, and write results back when done—is what truly turns creation into a scalable industrial process.