Forcing a server-side update to block

I just figure out the hard way that apparently even on the server side, some collection update calls are non-blocking, namely when they are not called through a Meteor.method() definition. What I mean by that is that in the code immediately following the collection.update() call, the data is still the old data, which is a clear sign the db update runs in the background and the update() call returned anyway. I can confirm this by adding a callback function and I find that inside the callback, the data is updated.

I am defining a couple of backend maintenance functions, defined inside a service class and called by shell or cron job. These functions are not and should not be called remotely, so they are not defined as Meteor methods. But they are in a server-only directory (/imports/server/)

Turns out they return immediately and don’t block until the database writes are complete. I verified this.

That is pretty… wrong for my use case. I want them to block. How do I force them to do so?

These are not Meteor jobs? Are they NodeJS?

If you use the non-callback (Promise) form of the standard npm MongoDB package, you could make use of async/await to ensure that your changes happen synchronously. They will never be strictly blocking. I don’t believe there’s a blocking version of the MongoDB package - that would be a tad crazy :wink:.

I am working entirely in Meteor. I just have maintenance methods that are not triggered by a client. Think “nightly cleanup”. At the moment I call them through the Meteor shell. In production, they will be called by a job.

I’m doing my update via the Meteor collection, e.g.

let collection = new Mongo.Collection('something')
...

class Service {
 static someFunction() {
 collection.update(id, { $set: { ... } })
}
}

In which case I’m not sure what you mean by “blocking”. In NodeJS that refers to blocking the event loop, which can be done for some operations, but not MongoDB AFAIK.

If you just want to prevent other access to the database, or collection(s), I suspect the best you’ll be able to do is set a global flag somewhere (in MongoDB itself, perhaps) that all other processes check before doing something.

edited the question to make it more clear.

what I mean is that after the update call, the data is not immediately updated. The call doesn’t block, it returns and the db update happens in the background, despite the Meteor documentation saying that server-side db updates should block.

I don’t mind if other processes access the db as well. I just need to continue working with the data after the update, and I can’t because I cannot be certain it has been updated already.

From what I can see in my investigation of this issue so far, the following code:

Warehouses.update({"_id":warehouse._id, "storage.resource": resource}, {
	$inc: {
		"storage.$.amount": amount
	}
})

updates the database, but it does not update the in-memory collection. It does correctly return the number of updated records. All this this looks quite odd behaviour to me. A bug, maybe? Or is my collection copied somewhere in a function call (the “warehouse” variable is passed through several function calls, but it should be an object, namely one element of the collection)

So, you’re saying that in the below code, the doc returned from the findOne does not have the expected change?

Warehouses.update({"_id":warehouse._id, "storage.resource": resource}, {
	$inc: {
		"storage.$.amount": amount
	}
})

const doc = Warehouses.findOne(warehouse._id);
console.log(doc);

No, from all I’ve tested, this works.

But warehouse is not updated, which greatly surprises me. Does find/findOne make a copy instead of giving me a reference?

For clarification, my code is roughly like this:

warehouse = ...findOne()
...do some calculations...
Warehouses.update()
...do some other calculations...
Warehouses.update()

in the 2nd (3rd, etc.) round of calculations, I’m still working with the old data. Re-fetching updated entities is the workaround I’m now using. It would be very cute if Meteor would ensure that my in-memory copy would also change when I update the database. After all, that’s what I thought would happen when I update through the collection instead of through raw Mongo calls.

I think you’re confusing this with Meteor’s livedata, where you can publish a cursor and have the client see the changes in near real time.

The workaround you describe is the correct way to do what you’re describing.

1 Like

yes, I think I got confused with that and thought all collections have live update functionality. Thanks for the help!