Skip to content

Instantly share code, notes, and snippets.

@rla
Created January 27, 2012 18:15
Show Gist options
  • Save rla/1690113 to your computer and use it in GitHub Desktop.
Save rla/1690113 to your computer and use it in GitHub Desktop.
Syncing data
function Server() {
this.rev = 0;
this.data = {};
this.changes = []; // { id, rev }
}
/**
* Finds changes after given revision. Returns array of {id, op, rev}.
*/
Server.prototype.changesAfter = function(rev) {
var changes = [];
var server = this;
this.changes.forEach(function(change) {
if (change.rev > rev) {
changes.push({
id: change.id,
op: server.data[change.id] === undefined ? 'D' : 'U',
rev: change.rev
});
}
});
return changes;
};
function Client() {
this.rev = 0;
this.data = {};
this.changes = []; // { id, rev }
}
Client.prototype.add = function(id, data) {
this.data[id] = data;
this.changes.push({ id: id, rev: this.rev });
};
Client.prototype.remove = function(id) {
delete this.data[id];
this.changes.push({ id: id, rev: this.rev });
};
Client.prototype.update = function(id, data) {
this.data[id] = data;
this.changes.push({ id: id, rev: this.rev });
};
/**
* Finds current changes. Returns array of {id, op, rev}.
*/
Client.prototype.currentChanges = function() {
var changes = [];
var client = this;
this.changes.forEach(function(change) {
changes.push({
id: change.id,
op: client.data[change.id] === undefined ? 'D' : 'U',
rev: change.rev
});
});
return changes;
};
Client.prototype.sync = function(server) {
var schanges = server.changesAfter(this.rev);
var cchanges = this.currentChanges();
// Apply server changes first.
// XXX must apply at the same time by pairing by id?
var client = this;
schanges.forEach(function(change) {
if (change.op === 'U') {
client.data[change.id] = server.data[change.id];
} else if (change.op === 'D') {
delete client.data[change.id];
}
});
// Apply client changes.
cchanges.forEach(function(change) {
if (change.op === 'U') {
server.data[change.id] = client.data[change.id];
} else if (change.op === 'D') {
delete server.data[change.id];
}
});
// Push rev.
server.rev += 1;
this.rev = server.rev;
// Transfer client changes with increased revision number.
cchanges.forEach(function(change) {
server.changes.push({ id: change.id, rev: client.rev });
});
// Cleanup.
this.changes = [];
};
function report(party, title) {
console.log(title);
console.log('\tRevision: ' + party.rev);
console.log('\tData:');
for (id in party.data) {
console.log('\t\t' + id + ': ' + party.data[id]);
}
console.log('\tChanges:');
party.changes.forEach(function(change) {
console.log('\t\tr' + change.rev + ' ' + change.id);
});
};
var s = new Server();
var c1 = new Client();
c1.add(1, 'd1-1.0');
c1.add(2, 'd2-2.0');
report(c1, 'Client 1 data before 1st sync');
c1.sync(s);
report(c1, 'Client 1 data after 1st sync');
report(s, 'Server data after 1st sync');
var c2 = new Client();
c2.sync(s);
report(c2, 'Client 2 data after 2nd sync');
c2.remove(1);
c2.add(3, 'd3-1.0');
report(c2, 'Client 2 data before 3rd sync');
c2.sync(s);
report(c2, 'Client 2 data after 3rd sync');
report(s, 'Server data after 3rd sync');
c1.sync(s);
report(c1, 'Client 1 data after 4th sync');
var c3 = new Client();
c3.sync(s);
report(c3, 'Client 3 data after 5th sync');
report(s, 'Server data after 5th sync');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment