I am using meteor.js. I want to call a thread that is independent of the thread that called it. In other words the spawned thread is unattended and does not return a value to the thread that spawned it. The spawned thread will update the database and meteor will take care of updating the client reactively
The reason I want independent/unattended threads is that I want my application to scale vertically. Imagine a situation where a million plus users are calling updateCompanyAmount (which updates a shared database collectionâs document and value) at the same time!! Some users connections might timeout while waiting for the lock to be released so they can update the shared value.
Here is my code. Please read the four comments in the code to quickly understand the code. Please give me a solution in javascript. Can an async call solve this issue? If so, why, and can you give me an example of meteor async method.
if (Meteor.isServer) {
Meteor.methods({
//I want to "save purchase history" and then return to client while a new thread or service-worker "updates company amount"
savePurchaseHistory: function(recharge,var1,var2,var3,var4,var5)
{
var userId = Meteor.userId();
.
.
.
PurchaseHistory.insert(recharge);
Meteor.call("updateCompanyAmount",companyAmount);
},
//I want to change this function to a service-worker or Meteor microservise
updateCompanyAmount: function(companyAmount,balance) {
Meteor.call("updateCompanyAmountRecursively",companyAmount);
},
//CompanySharedNumbers collection can be updated by multiple users. In this example companyAmount in CompanySharedNumbers is being updated
//by multiple users using a lock!
updateCompanyAmountRecursively: function(companyAmount) {
var userId = Meteor.userId();
var user = Meteor.users.findOne({_id: userId});
var random = Math.random();
var companySharedNumbers = CompanySharedNumbers.findOne({_id: 'onlyOccurrence'});
var locked = companySharedNumbers.updateLock;
if (!locked)
{
CompanySharedNumbers.update({_id: 'onlyOccurrence',updateLock:locked},{$set:{updateRandomNumber:random,
updateLock:!locked,updatedBy:user.username}});
Meteor.users.update({_id: userId},{$set:{updateRandomNumber: random}});
}
user = Meteor.users.findOne({_id: userId});
var userRandomNumber = user.updateRandomNumber;
companySharedNumbers = CompanySharedNumbers.findOne({_id: 'onlyOccurrence'});
var randomNumber = companySharedNumbers.updateRandomNumber;
var updatedBy = companySharedNumbers.updatedBy;
locked = companySharedNumbers.updateLock;
if (locked)
{
if (randomNumber == userRandomNumber && updatedBy == user.username)
{
if (companySharedNumbers.companyAmount)
{
companyAmount = companyAmount + companySharedNumbers.companyAmount;
}
random = 2;
CompanySharedNumbers.update({_id: 'onlyOccurrence',updateRandomNumber:userRandomNumber,updateLock:locked,updatedBy:user.username},{$set:
{updateRandomNumber:random,updateLock:!locked,companyAmount:companyAmount,updatedBy:user.username}});
}
else
{
Meteor.call("updateCompanyAmountRecursively",companyAmount);
return false;
}
}
else
{
Meteor.call("updateCompanyAmountRecursively",companyAmount);
return false;
}
}
});
}
Also, itâs probably worth mentioning that (until very recently) threads in NodeJS werenât really a thing (NodeJS is single-threaded), so if you really did need a separate thread youâd spawn another process.
@ cloudspider, in the example, what happens if the user closes the browser after signUp before Meteor.defer(function () {Email.send(/* email object */);}) is executed. Will the email sending happen or will it not?
With Meteor.defer my recursive function always stops at ârecursive function loopâ 610 for some reason. I tried @robfallows async function and I close the browser the recursive function continues to run in the background as per my use case requires. I even looped to 10000, no problem. The only problem is that
var userId = Meteor.userId();
returns âundefinedâ inside the async function!! Is there a way to get the userId inside the async function?
In the above example it would show 10 times the number 10! That is because of the setTimeout function is only triggered after the loop has finished which means that i was updated already to 10.
To fix this you would need to push the variable to the timeout function:
Meteor.methods({
this.unblock(); // Allow client to do other method calls too
const userId = this.userId; // Notice I'm using this.userId instead of Meteor.userId() since this.userId comes from the server while Meteor.userId comes from the client
for(let i=1; i<=10; i++) {
Meteor.defer(() => {
Meteor.call('someLengthyOperation', userId);
});
}
});