-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Race condition in the replication algorithm #986
Comments
Can't we handle this through remote hook? |
No. Consider the following sequence of execution of two parallel requests updating the same record (model instance):
|
@bajtos How about |
@violet-day Yes, I was thinking about implementing something similar. We already have a checksum of the data, this property is even better than version number. The trick is to design and implement a database-agnostic API in juggler that can be mapped to an atomic operation in all SQL and NoSQL databases (connectors). A possible solution is to use Model.updateAll(
{ id: modelId, rev: baseRev }, // update only if the rev (checksum) was not changed
{ id: modelId, rev: newRev, /* the properties to update */ },
function(err, updatedCount) {
if (err) return cb(err);
if (updatedCount === 1) return cb(); // success
// conflict, the database was updated under our hands
// restart the replication algorithm
}
); This is still not a complete solution. If a non-loopback client modifies the database after we calculated (*) Embedding the Change object inside the Model data may not be possible in the situation where the user wants to enable change tracking on top of an existing (legacy) database where he cannot alter the existing tables. I guess we don't have to support this scenario in the first version. |
For v1.0, I would like to implement the following solution, which should be bullet-proof and support all existing replication use-cases, although at the cost of lower performance. Later in the future, we can implement optimised variants.
The initial implementation of the last step (unoptimised but generic) for a single model instance:
When a conflict is detected, we should probably run "rectify change" for the conflicted model. In many cases, it may be possible to automatically repeat the bulkUpdate() and/or replicate() until it passes. However, this will be probably omitted from the initial implementation for v1.0. @ritch @raymondfeng Thoughts? I have also one meta question for you. Should we run Operation hooks for the DAO calls made from Another question is how to deal with ACLs, I have opened a new issue for that: #1166 |
The implementation outlined in my previous comment depends on #1167. |
Timeboxing the story to 5 points, I'll focus on fixing the race condition and leaving out the UX/ease-of-use for a follow-up story if I run out of time. |
This still leaves a small hole, because the "data" where-filter checks only that no existing properties were modified, it cannot ensure that no new properties were added. This issue should hopefully apply only to non-strict mode where users can add arbitrary dynamic properties to model instances. Depending on how the MongoDB connector persists and queries undefined properties, it may be affected too. |
When two clients are preforming replication at the same time and the same object was changed in both clients, some of the changes may get lost. The replication algorithm should check the data before the update to verify it was not changed by another party during the replication process.
This will most likely require LoopBack to support transactions for SQL datasources and [Update if Current](http://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/#update-if-current] for MongoDB.Another example are external changes. At the moment, there is a timer-based hook to detect any changes made by non-loopback applications and update change tracking records. If the sync algorithm is started before this timed check was run, changes made by the external writer may get discarded.
The text was updated successfully, but these errors were encountered: