allow packages to block migration until they're ready

This commit is contained in:
Geoff Schmidt
2012-03-01 17:24:13 -08:00
parent 79aabd797b
commit ddfbf236ce
4 changed files with 66 additions and 45 deletions

View File

@@ -97,14 +97,14 @@ Meteor._LivedataConnection = function (url) {
self.outstanding_methods = reload_data.outstanding_methods;
// pending messages will be transmitted on initial stream 'reset'
}
Meteor._reload.on_migrate(reload_key, function () {
Meteor._reload.on_migrate(reload_key, function (result) {
var methods = _.map(self.outstanding_methods, function (m) {
// filter out callback
return {msg: m.msg};
});
return { next_method_id: self.next_method_id,
outstanding_methods: methods };
result({ next_method_id: self.next_method_id,
outstanding_methods: methods });
});
// Setup stream

View File

@@ -68,16 +68,18 @@
}
var save_callbacks = {};
var providers = [];
////////// External API //////////
// Called by packages when they start up.
// Registers a callback for when we want to save data.
// Before a reload, callback is called, and should return
// a JSONifyable object.
// Before a reload, callback is called. It takes one argument, a
// function. The package should wait until it is ready to migrate,
// and then call the function. If it has migration data, it should
// pass it to the function as a single JSON-compatible argument.
Meteor._reload.on_migrate = function (name, callback) {
save_callbacks[name] = callback;
providers.push({name: name, callback: callback});
};
// Called by packages when they start up.
@@ -86,36 +88,55 @@
return old_data[name];
};
// Trigger a reload. Calls all the callbacks, saves all the values,
// then blows up the world.
// Trigger a reload. Starts a process that asynchronously calls all
// the callbacks, saves all the values, and then terminates this VM
// and starts a new one.
var reloading = false;
Meteor._reload.reload = function () {
// Meteor._debug("Beginning hot reload.");
if (reloading)
return;
reloading = true;
// ask everyone for stuff to save
var new_data = {};
_.each(save_callbacks, function (callback, name) {
new_data[name] = callback();
});
// ask everyone for stuff to save, asynchronously
var migration_data = {};
var remaining = _.clone(providers);
var requestNext = function () {
var next = remaining.shift();
if (!next)
saveAndRestart();
else
next.callback(function (value) {
if (value !== undefined)
migration_data[next.name] = value;
requestNext();
});
};
var new_json;
try {
new_json = JSON.stringify({
time: (new Date()).getTime(), data: new_data, reload: true
});
} catch (err) {
Meteor._debug("Asked to persist non-JSONable data. Ignoring.");
new_json = '{}';
}
// then persist the migration data and restart
var saveAndRestart = function () {
var json;
try {
json = JSON.stringify({
time: (new Date()).getTime(), data: migration_data, reload: true
});
} catch (err) {
Meteor._debug("Asked to persist non-JSONable data. Ignoring.");
json = '{}';
}
// save it
if (typeof sessionStorage !== "undefined") {
sessionStorage.setItem(KEY_NAME, new_json);
} else {
Meteor._debug("Browser does not support sessionStorage. Not saving reload state.");
}
// save it
if (typeof sessionStorage !== "undefined") {
sessionStorage.setItem(KEY_NAME, json);
} else {
Meteor._debug("Browser does not support sessionStorage. Not saving reload state.");
}
// blow up the world!
window.location.reload();
// Restart with the new code.
window.location.reload();
};
// kick off asynchronous process
requestNext();
};
})();

View File

@@ -105,9 +105,9 @@ Session = _.extend({}, {
if (Meteor._reload) {
Meteor._reload.on_migrate('session', function () {
Meteor._reload.on_migrate('session', function (result) {
// XXX sanitize and make sure it's JSONible?
return { keys: Session.keys };
result({keys: Session.keys});
});
(function () {

View File

@@ -130,21 +130,21 @@ _.extend(Meteor._Stream.prototype, {
// inspect the welcome data and decide if we have to reload
try {
var welcome_data = JSON.parse(welcome_message);
if (welcome_data && welcome_data.server_id) {
if (self.server_id && self.server_id !== welcome_data.server_id) {
Meteor._reload.reload();
// world's about to end, just leave the connection 'connecting'
// until it does.
return;
}
self.server_id = welcome_data.server_id;
} else {
Meteor._debug("DEBUG: invalid welcome packet", welcome_data);
}
} catch (err) {
Meteor._debug("DEBUG: malformed welcome packet", welcome_message);
}
if (welcome_data && welcome_data.server_id) {
if (self.server_id && self.server_id !== welcome_data.server_id) {
Meteor._reload.reload();
// world's about to end, just leave the connection 'connecting'
// until it does.
return;
}
self.server_id = welcome_data.server_id;
} else
Meteor._debug("DEBUG: invalid welcome packet", welcome_data);
// update status
self.current_status.status = "connected";
self.current_status.connected = true;