How to get connection count to a specific stream within ActionCable? - javascript

Background: I'm working on an app that has a JS frontend, and a Rails backend. I've been able to successfully create a vanilla JS websocket client, that properly connects to and receives broadcasts from a Rails Actioncable-enabled websocket server.
The Problem and TLDR:
I'm trying to get the total count of clients that connected and passed in specific data upon connecting.
Details:
For example, say I have something like this:
//Client waves to server, says where they are from by passing in country to say_hi method on backend
const wave = {
command: 'message',
identifier: JSON.stringify({
channel: 'CountryChannel',
}),
data: JSON.stringify({
action: 'say_hi',
country: "USA",
}),
};
socket.send(JSON.stringify(wave));
Respectively, on the backend I have a say_hi method that is able to get my passed in country value.
class CountryChannel< ApplicationCable::Channel
def subscribed
stream_from 'country'
end
def say_hi(data)
#country = data['country'] ##country = "USA"
end
end
So I now want to be able to somehow see the amount of waves from "USA".
I'm a complete beginner in Rails, and have been going down the ActionCable docs rabbit-hole. I tried searching SO for anything like this online and found:
ActionCable - how to display number of connected users?
My problem with this is the approved answer (along with its caveats) would show the count of "all" the connection on that stream, whereas I want to segment that based on the data.
On the same link, I also saw an answer where you could use the :identified_by param in Connection object. My issue with that is I just can't figure out anywhere how to set that from my client. Again, I'm a noob at all this so it's probably straightfoward, but I've just been unable to connect the dots.
I know that I could approach this by creating specific streams based on a parameter and have something like:
stream_from "country#{#country}" # streams country#USA
But I'm lost as to see the amount of clients that are currently receiving broadcasts on this stream, or something like that. Specifically, I just don't know what to use from ActionCable to be able to do this.
Any ideas? I'd love to further specify or provide any more info if needed. Just feel free to ask. Thanks!

Related

How to remove particular messages in rabbitmq before publishing new messages?

I have a subscriber which pushes data into queues. Now the messages looks this
{
"Content": {
"_id" ""5ceya67bbsbag3",
"dataset": {
"upper": {},
"lower": {}
}
}
Now a new message can be pushed with same content id but data will be different. So in that i want to delete the old message with same id or replaece the message those id is same & retain only latest message.
I have not found a direct solution for this in rabbitmq. Please guide me how we can do this ?
I have already gone through some posts.
Post 1
Post 2
What you are trying to achieve cannot be trivially solved with RabbitMQ (or rather the AMQP protocol).
RabbitMQ queues are simple FIFO stacks and don't offer any mean of access to the elements beyond publishing at their top and consuming from their bottom.
Therefore, the only way to "update" an already existing message without relying on an another service would be to fetch all the messages until you find the one you are interested in, discard it, and publish the new one with the other messages you fetched together with it.
Overall, the recommendation when using RabbitMQ in regards of message duplication is to make their consumption idempotent. In other words, the consumption of 2 messages deemed to be the same should lead to the same outcome.
One way to achieve idempotency is to rely on a secondary cache where you store the message identifiers and their validity. Once a consumer fetches a new message from RabbitMQ, it would check the cache to see if it's a valid message or not and act accordingly.
I think this is a slightly wrong way to use rabbitMQ.
only immutable (not intended to change) tasks should be put into queues which a worker shall consume.
An alternative way to implement your particular task is
just push immutable data into queue { "content" : { "_id" : "5ceya67bbsbag3"} .. }
store mutable data in db (mongo) or in-mem db (something like redis is suggested here).
whenever update needed, update in db
let your worker fetch required data using your "_id" ref from the db
I am not sure if removing a message is a good idea. If your requirement is to update the data as it comes so that always latest data is maintained for same Id.
Other thing is as messages are getting consumed always the last message data will get updated. So I don't see a issue here in Rabbit MQ.

Firebase/NoSQL schema for an instant messaging system

I'm using Firebase for an app and the built-in real-time capabilities seem well suited for instant messaging. I'm just having a hard time working out in my head how the database should be set up. Ideally, it's something like this:
messages: {
<messageId>: {
from: <userId>,
to: <userId>,
text: <String>,
dateSent: <Date>
dateRead: <Date>
}
}
And that's all fine for sending messages, but reading message threads becomes difficult. I need to query the (potentially huge) list of messages for messages that match the current thread's sender and receiver, and then order those by dateSent. If that is possible with Firebase's new querying API, then I have yet to figure out exactly how to do it.
Querying a huge list of messages is never a good idea. If you want a fast-performing Firebase/NoSQL application, you'll need to model the data to allow fast look up.
In a chat scenario that typically means that you'll model your chat rooms into the data structure. So instead of storing one long list of messages, store the messages for each chat "room" separately.
messages
<roomId>
<messageId1>: "..."
<messageId2>: "..."
<messageId3>: "..."
Now you can access the messages for the chat without a query, just ref.child(roomId).on(....
If you want a persistent mapping that ensures the same two users end up in the same room, have a look at Best way to manage Chat channels in Firebase

Meteor Random package vs randomSeed

I'm trying to replicate the latency compensation done by Meteor and minimongo. For instance to create an id on the client and then the same id on the server after calling the method, so the client can update the UI without waiting for the server response.
For this I need to generate the same Id on both the client and the server.
So, in meteor if I do: Random.createWithSeeds('abc').id()
I always get:
WKrBPwCSbzNHmhacn
But if I connect from and external app, outside of metor using a ddp client:
self.send({msg: 'method', id:id, randomSeed: 'abc', method: name, params: params});
I get a different Id. It's repeatable, but not the same as the one generated by Random. Why?
I cannot understand. Are they using a different generationId algorithm?
Packages I'm using:
On Meteor: https://atmospherejs.com/meteor/random
On external Client (outside Meteor): https://github.com/eddflrs/meteor-ddp + source code of random.js
This may not be a complete answer (I'm still looking too), but the way you're using Random.createWithSeeds should read:
> let generator = Random.createWithSeeds('abc')
> generator.id()
'WKrBPwCSbzNHmhacn'
> generator.id()
'h6iLWkdEfZ7wXWpPQ'
Perhaps an edit might clarify that createWithSeeds('abc') is supposed to return an object you call .id() on multiple times. I've never tried passing the seed from another ddp client though, and I'll let you know when I do

Couchdb / Pouchdb Relation between multiple users and multiple documents

I have a problematic here:
I'm builing a mobile app with ionic frmaework who needs to be able to work offline.
I'd like to adopt the CouchDB / PouchDB solution. But to do that i need to know how to put my data in the noSQl datatbase (MySQL user before ...). So, nosql is new to me but it seems interesting.
So my app has a connection part so a user database. And each user has documents who are attached to him. But many users can have many documents (sharing documents). And I want to replicate the data of one user (so his information + his documents on the mobile app).
What I thought is this:
One database per. One database for all Document with a server filtering to send only the documents that belongs to the user.
And on the client side I'd juste have to call :
var localDB = new PouchDB("myuser");
var remoteDB = new PouchDB("http://128.199.48.178:5984/myuser");
localDB.sync(remoteDB, {
live: true
});
And like that on the client side I'd have something like that :
{
username: "myuser",
birthday : "Date",
documents : [{
"_id": "2",
"szObject": "My Document",
},
{
"_id": "85",
"szObject": "My Document",
}]
}
Do you think something like that is possible using Couchdb and pouchdb, and if yes, am I thinking about it the right way?
I read it's not a problem to have one database per document, but I don't know if the replication will work like I imagine it
Plain CouchDB doesn't have any per-document access options, but these could be your solutions:
A. Create a View, then sync Pouch-To-Couch with a filter. But although this will only sync the documents that the user is supposed to see, anyone with enough knowledge could alter the code and view someone else's documents or just do anything with the database actually (probably not what you're looking for).
B. Create a master DB with all documents, then a database for each user, and a filtered replication between the master & per-user-dbs. Probably the simplest and most proper way to handle this.
C. Unfortunately there isn't a validate_doc_read (as there is a validate_doc_update) but perhaps you could make a simple HTTP proxy, which would parse out incoming JSON, check if a particular user can view it and if not, throw a 403 Forbidden. Well you'd also have to catch any views that query with include_docs=true.
(late reply, I hope it's still useful - or if not, that you found a good solution for your problem)

Changing a Meteor collection subscription for all clients

I am developing a webapp in which I'd need one client, associated with the admin, to trigger an event (e.g., a new value selected in a dropdown list) which in turns will tell all the other connected clients to change the subscription, possibly using a parameter, i.e., the new selected value.
Something along the lines of
Template.bid.events
"change .roles": (e, tpl) ->
e.preventDefault()
role = tpl.$("select[name='role']").val()
Meteor.subscribe role
Of course this works for the current client only.
One way I thought would be keeping a separate collection that points a the current collection to be used, so the clients can programmatically act on that. It feels cumbersome, thou.
Is there a Meteor-way to achieve this?
Thanks
In meteor, whenever you have a problem that sounds like: "I need to synchronize data across clients", you should use a collection. I realize it seems like overkill just to send one piece of data, but I assure you it's currently the path of least resistance.
There are ways you can expose pseudo-collections which don't actually write to mongo, but for your use case that really sounds like overkill - new Mongo.Collection is the way to go.
You can use streams to setup a simple line of communication between connected clients and the server. It doesn't store data in MongoDB. Just let all connected clients listen to a stream and switch subscriptions when a new message comes in with the subscription name. Make sure only your client associated to your admin can push messages to the stream.
Available package: https://atmospherejs.com/lepozepo/streams
Examples: http://arunoda.github.io/meteor-streams/

Categories

Resources