I'm writing a Meteor app which allows clients to execute terminal commands on the server at the click of a button.
I know how to do this with a single command:
//server
Meteor.methods({ exec : cmd => { ... } })
//client
Meteor.call('exec', cmd, (err, result) => {
console.log(result)
})
But now I'm trying to implement a more complex protocol and don't quite know what the best way is. I want the client to kick off a series of commands, have the server run them and tell me, step by step, whether they succeeded or failed.
Obviously I could implement this with the above code by writing client-side code that runs exec with the first command, checks the result from the server, runs exec with the next command and so on.
The crux is that in my case the series of commands is always the same, so it would make much more sense to only do one Meteor.call on the client -- the server would know what commands to run. However I would also like to have the results of the individual commands available on the client as they come in -- and this is what I can't do, because Meteor.call only returns once, of course.
What I'm looking for is a sort of stream or iterator through which I can send a number of messages to the client until everything is done. I've seen some outdated packages called meteor-streams and similar that might be able to do something like that, but I'm thinking there must be a smart way in Meteor itself to solve this. Ideas?
A common solution is a Notifications collection. Create the collection with a schema: for: ${userid}, msg: ${msg string}, type: ${err success etc}. Create a Notifications publication, which publishes docs with the users userid.
You can then subscribe to the Notifications collection in some main template page on the client. Use observeChanges to look for changes to the collection and either console.log them, use JavaScript to display them on the page or simply install a package like sAlerts to handle them.
Inside the observe changes callback, a seenNotification method should be called which removes the notification from the db, so it is not shown again.
I'll post code snippets a bit later.
Have a look at this: https://github.com/RocketChat/meteor-streamer
I think it will solve your problem easily.
Related
Really bizarre that Firebase doesn't seem to work quite like typical Express app. Whatever I write in Express and copy-paste to Firebase Functions I typically get error. There is one that I can't figure out on my own though.
This endpoint is designed to start a function and live long enough to finish even longer task. That request is a webhook (send docs, we will transform them and ping you when it's done to specified another webhook). Very simplified example below:
router.post('/', (req, res) => {
try {
generateZipWithDocuments(data) // on purpose it's not async so request can return freely
res.sendStatus(201)
} catch (error) {
res.send({ error })
}
})
On my local machine it works (both pure Express app and locally emulated Firebase Functions), but in the cloud it has problems and even though I put a cavalcade of console.log() I don't get much information. No error from Firebase.
If generateZipWithDocuments() is not asynchronous res.sendStatus() will be immediately executed after it, and the Cloud Function will be terminated (and the job done by generateZipWithDocuments() will not be completed). See the doc here for more details.
You have two possibilities:
You make it asynchronous and you wait its job is completed before sending the response. You would typically use async/await for that. Note that the maximum execution time for a Cloud Function is 9 minutes.
You delegate the long time execution job to another Cloud Function and, then, you send the response. For delegating the job to another Cloud Function, you should use Pub/Sub. See Pub/Sub triggers, the sample quickstart, and this SO thread for more details on how to implement that. In the Pub/Sub triggered Function, when the job is done you can inform the user via an email, a notification, the update of a Firestore document on which you have set a listener, etc... If generateZipWithDocuments() takes a long time, it is clearly the most user friendly option.
I am trying to implement some methods for a DDP API, for use with a C# remote client.
Now, I want to be able to track the connection to implement some type of persistent session, to this end, id hope to be able to use the session id given by DDP on connection, example:
{
"msg": "connected",
"session": "CmnXKZ34aqSnEqscR"
}
After reading the documentation, I see that inside meteor methods, I can access the current connection using "this.connection", however, I always get an undefined "this.connection".
Was it removed? If so, how can i access it now?
PS: I dont want to login as a user and access this.userId, since the app I want to create should not login, but actually just get a document id and do work associated with that, including changes to other collections, but all, regarding ONLY this id, and I dont want to have to include this id every time I call a function, since, this could possibly lead security problems if anyone can just send any id. The app would ideally do a simple login, then associate token details with his "session".
Changing from:
() => { this.connection; }
to:
function() { this.connection; }
solves the problem from me. Based on a comment in the accepted answer.
The C# client on github has a few bugs with it as it doesn't follow the DDP spec exactly. When you send commands to it to connect and run a call, it usually sends the '.call' too soon.
The method does work if you do it this way with this.connection on the server side Meteor method.
You need to make sure you send the method calls after you know that you are actually connected. This is what works at least with Meteor 0.8.2
I was using a file named ".next.js" to force meteor to use the newest unsupported javascript spec using a package.
Somehow this messed it up. Changed back to default javascript and it now works.
Thank you :)
init.coffee
Meteor.startup ->
# client init
if Meteor.isClient
Meteor.call "init"
methods.coffee
Meteor.methods
init: ->
console.log #connection.httpHeaders.host
it's that easy...
Meteor is said to automagically (in most cases) figure out what code to run on the client and what code to run on the server so you could theoretically just write all your code in one .js file.
I would like to be able to write code in my browser console and have it executed pretty much as if I had put the code in a file on my server.
For example, in my browser console:
[20:08:19.397] Pages = new Meteor.Collection("pages");
[20:08:30.612] Pages.insert({name:"bro"});
[20:08:30.614] "sGmRrQfezZMXuPfW8"
[20:08:30.618] insert failed: Method not found
Meteor says "method not found" because I need to do new Meteor.Collection("pages"); on the server.
But is there a workaround for this, whether using the above-mentioned automagic or by explicitly saying in my browser console "run the following line of code on the server!"?
Well it doesn't "automagically" figure it out - you have to very explicitly do one of two things:
Separate the code into client and server directories.
Wrap the code in an isClient or an isServer section.
Otherwise, any code you write will execute in both environments. However, any code input by the user on the client will only be executed on the client. Meteor has been specifically designed to protect this boundary.
You can call a method on the server from the client, but again the server cannot be tricked into executing client-defined functions.
In your specific example, you can always define the collection only on the client like so:
Pages = new Meteor.Collection(null);
That will allow you do freely manipulate the collection data on the client, but it will not involve the server (nothing will be stored in the db).
We have a problem with our meteor server. When we publish 300 or so items with Meteor.publish/Meteor.subscribe the server increases its memory and eventually becomes unresponsive.
We thought of:
1) monitor the number of reactive subscribtions / memory taken by an active subscription
2) make something like ,,one time publish" - ignore changes in server side collection
Any thoughts on how any of the above can be accomplished ?
Or any other tips to debug /improve meteor app performance ?
Thanks
zorlak's answer is good.
Some other things:
You can do a one-time publish by writing your own custom publisher via the this.set API, based on the code in _publishCursor. You'd do something like:
Meteor.publish("oneTimeQuery", function () {
MyCollection.find().forEach(function (doc) {
sub.added("collectionName", doc._id, doc);
});
sub.ready();
});
This does a query, sends its results down, and then never updates it again.
That said, we hope that Meteor's performance will be such that this is unnecessary!
I'd also like to add an easy way to get stats (like number of observed cursors) from an app to Meteor (exposed as an authenticated subscription) but haven't had the time yet.
As of Meteor 0.5.1, one thing you can do is to remove dependencies on the userId from the publish function. If a publish function does not depend on which user is subscribing, then Meteor will cache the db query so it doesn't get slower as more users subscribe.
See this Meteor blog post: http://meteor.com/blog/2012/11/20/meteor-051-database-scaling
I am trying to understand when I might use this.flush() in a Meteor application.
The docs state the following
Call inside publish function. Sends all the pending set, unset, and
complete messages to the client.
If my publish function is something like this
Meteor.publish('myCollection', function(myid){
return MyCollection.find({_id: myid});
});
would I use this.flush()?
What kind of case would one use this.flush() in?
Thanks
S
Not needed in that use case, because Meteor.publish automatically handles the details of how to turn Mongo cursors into the appropriate set and unset commands for each subscribed client.
If you write a custom publish that manages its own set and unset, you can use flush to push all pending changes down to the client. You'll find an example of that technique here: How does the messages-count example in Meteor docs work?