How to prevent a client race condition between Meteor.userId() and subscription updates that depend on userId? -
i seeing repeatable issue user authenticates ("logs in") meteor server, , client subscription depends on userid
updated (and dependent ui templates reactively update) before meteor.userid()
registers successful login.
for example, in code snippet, assert throw
:
var coll = new meteor.collection("test"); if (meteor.isserver) { meteor.publish('mineorpublic', function () { // publish public records , owned subscribing user return coll.find({owner: { $in: [ this.userid, null ]}}); }); } if (meteor.isclient) { var sub = meteor.subscribe('mineorpublic'); var cursor = coll.find({}); cursor.observe({ added: function (doc) { if (doc.owner) { // should true?! assert(doc.owner === meteor.userid()); } } }); }
analogous added
function above, if write template helper checks meteor.userid()
, see value of null
, when invoked data context of document owner.
there apparently race condition between meteor collection pub/sub , account userid
update mechanisms. seems me meteor.userid()
should updated before subscriptions update based on change in this.userid
in server publish function, reason opposite seems true (that is, assert
in code above throw).
the reason care because have packages depend on obtaining valid meteor authentication token (using accounts._storedlogintoken()
) on client use in securing http requests files stored on meteor server. , authentication token isn't correct until meteor.userid()
is. flow of events goes this:
- user logs in
- publish function on server reruns based on change in
this.userid
. - client begins receiving new documents corresponding change in userid.
- ui template reactively updates add dom elements driven new documents
- some of dom elements
<img>
tagssrc=
values depend on data context. - http requests triggered , fail
403
(forbidden) errors because required authentication cookie hasn't been set yet. meteor.userid()
updates on client, , code reactively runs set authentication cookie- helpers in template depend on session variable set in cookie update code rerun, dom doesn't change, because urls in
<img>
tags don't change. - because dom doesn't change, tags don't retry failed attempts load images.
- everything settles down, , user has manually reload page images appear.
i've come 2 possible approaches work around issue:
in template helper generates url
<img>
tag, append dummy query string such as:"?time=" + new date().gettime()
. causes dom change every time helper called , fixes problem, screws-up browser caching , if not coordinated cause assets unnecessarily load multiple times, etc.in every template helper touches document data add test of:
if (this.owner && this.owner !== meteor.userid()) { // perhaps meteor.loggingin() used above? // invalid state, output placeholder } else { // valid state, output proper value template }
i hope knows of less kludgy way work around this. alternatively, if consensus arises bug , meteor's behavior incorrect in respect. happily file issue on github. enjoy working meteor, kind of gritty annoyance grinds in gears of "it works".
thanks , insights.
after trying lots of things, variation on example code in op seems consistently solve race condition, , find acceptable resolution, unlike initial attempted workarounds.
i still feel kind of logic should unnecessary , welcome other approaches or opinions on whether meteor's behavior in op sample code correct or erroneous. if consensus emerges in comments meteor's behavior wrong, create issue on github this.
thanks additional feedback or alternative solutions.
var coll = new meteor.collection("test"); if (meteor.isserver) { meteor.publish('mineorpublic', function (clientuserid) { if (this.userid === clientuserid) { // publish public records , owned subscribing user return coll.find({owner: { $in: [ this.userid, null ]}}); } else { // don't return user owned docs unless client sub matches return coll.find({owner: null}); } }); } if (meteor.isclient) { deps.autorun(function () { // resubscribe anytime userid changes var sub = meteor.subscribe('mineorpublic', meteor.userid()); }); var cursor = coll.find({}); cursor.observe({ added: function (doc) { if (doc.owner) { // should true?! assert(doc.owner === meteor.userid()); } } }); }
this code works giving server publish function information needs recognize when running ahead of client's own login state, thereby breaking race condition.
i think meteor should automatically: clients should not see documents based on changes this.userid
in publish function until after client meteor.userid()
has been updated.
do others agree?
Comments
Post a Comment