Implement facebook style status message system in mongodb - node.js

How can we implement a Facebook like status message system in mongodb (using mongoose), where whenever any given user posts his status it gets broadcasted on all his friends timeline.
It doesn't have to be real-time, there will be a refresh button to get the latest statuses.
here is what I have come up with:
Plan A:
status(collection)
id, user_id(reference), status_msg
Benefit: faster write speed
Plan B:
status(collection)
id, user_id(reference), status_msg, friends_list[sub-document]
Benefit: faster read speed
With plan A, I'll have to loop through all the friends a user has in his friends list and then fetch all the status.
I'll have to do this every time (page refresh/ new login) for every single friend.
With Plan B, I'll only have to fetch the statuses which has the current user in the friends_list.
I would like to know your opinion and suggestion on this ?
Is there any better way of approaching this problem ?
I would also like to know how I can use rabbitMQ here to increase the efficiency and lower the unnecessary db i/o .

Assuming that each user will likely have several friends, and these friends refresh their timeline several times a day, you can assume that reading will happen much more frequently than writing. That means from a pure performance standpoint you would optimize for read-access, not for write-access, and store the receivers with the message.
However, keep the semantics in mind. What if the friend-list of the author changes after they posted a status message?
Do you want the message to disappear from the timelines of any ex-friends?
Do you want the message to appear in the timeline of any new friends they make?
When the answers to these questions are yes, you should rather determine the receivers on read than on write.
There is also a third option which might be worth considering: Do not handle messages by sender, handle them by receiver. When someone posts a message, create an individual copy of the message for each of their friends and save them as separate documents. You can then get all messages for a user by querying your messages collection for messages where they are the receiver. The friend/unfriend operation would then need to check for any messages which need to be added/removed. The major drawback of this approach would be that users with a very high number of friends would create a very high write-load when posting something.

Related

How to manage the conversation flow if face timeout limit (5 seconds) in Dialogflow / Api.ai?

I am making a bot on Dialogflow with a Fulfillment. Considering the given strict 5-second window in DialogFlow, I am getting [empty response] as a response.
I want to overcome this issue, but my web service requires more than 9 seconds for the execution.
I am considering to redesigning the conversation flow in such a way that we will start streaming audio till the Response is processed.
Example:
User Question: xx xxx xxx xxxx xxxxx?
Response: a). We'll play fixed audio to keep the user engaged for few seconds till it finds a response text in the back end; b).
Receive answers from the web service and save them in the session to
display further.
How can I achieve this and how can I handle the Timeout issue?
You're on the right track, but there are a number of other things to consider.
First, however, keep in mind that anything that is trying to "avoid" the 5 second timeout already indicates some issues with the design. Waiting 10 seconds for a reply is a pretty long time with something as interactive as voice! Even 5 seconds, which is the timeout, is a long time. (And there is no way to change this timeout.)
So the first thing you may want to do is consider if there is a better/faster way to do what you want.
If not, the rough approach would be something like this:
Get the request from the user.
Track a unique identifier, either tied to the user or tied to the session. You'll be using this as a key into some kind of database or data store.
Start the API call as part of an asynchronous request or in another thread.
Reply immediately that you're working on it in a way that the user will send another request. (See below for this issue.) You'll want to make sure that the ID is maintained as part of this session - so you'll need to save it as part of the Session data.
At this point - you're basically doing two things in parallel.
When the API call completes, it needs to save the result in the datastore against the identifier. (It can't save it in the session itself - that response was already sent back to the Assistant.)
You're also waiting for a reply from the user. When it comes in:
Check to see if you have a response saved for this session yet.
If not, then go back to step 4. (You may want to track how many times you get here and give up at some point.)
If you do have the result, reply to the user with the information.
There is an issue with how you reply in step 4, since you want to do something that will guarantee you another request from the person expecting an answer. There are a few possible approaches:
The most straightforward way would be to send back a Media response to play a few seconds of "hold music". This has the advantage that, when the music stops, it will send an event to Dialogflow which you can capture as an Intent and then continue with step 5.
But there are some problems:
Not all versions of the Assistant support the Media response. You will need to check to confirm the feature is supported before you use it and, if not, use another approach (see below).
The media player that is presented on some Assistants allow the user to stop playback, or will not correctly send an event when the audio stops in some situations. So you may never get another request in this session.
Another approach involves some more advanced conversation design tricks, so may not always be suitable for your conversation. Your response can say that you're looking up the results but then ask the user a question - possibly one that is related to other information that you will need. With their reply, you can collect this information (if you need it) and then see if you have a result yet.
In some conversations - this works really well. For example, if you're looking up flights to somewhere, while you're looking that up you might ask them if they will need a hotel or rental car, which you might ask about anyway.
Other conversations, however, don't easily have such questions. In these cases, you may need to ask something that isn't relevant while you stall for time.

How should I manage the number of sockets in a node.js application?

I am building my first web-based node.js application - an online game - as a hobby/project to try and teach myself how it all works.
I'm using socket.io to send real-time updates (who's in the lobby, points scored etc) to users, but I'm not sure whether the way I'm managing the sockets, and the information being sent through them, in the best way.
Whenever the game is updated, I'm sending an object to each user which updates everything at once, and a lot of the time, the information being updated is actually staying the same. For example, if a user scores a point, an update is sent to everyone's browser to update the leaderboard, but that same socket.on function is re-sending information such as usernames, which stay the same throughout the game:
exampleObject = {
"usernames" : [username1, username2], // only gets updated in the browser once, but is sent every time
"points": {
"username1": 1, // Different value with every update
"username2": 3
}
}
(The real object is quite a bit bigger than this)
Would it be more sensible to have a different socket.on function for every individual piece of information which needs updating, so I can then call them individually as and when required, or is there any sense in updating everything through one function? Any thoughts/advice would be greatly appreciated.
If you are regularly sending a piece of information over and over, then it makes sense to design a specific message that only contains that specific information so you aren't regularly sending information that does not need to be sent. You can have as many different messages as you want and you should use that to design efficient messages, particularly for the most common messages.
Would it be more sensible to have a different socket.on function for every individual piece of information which needs updating, so I can then call them individually as and when required
Yes. Design efficient messages specifically for things you regularly send.
or is there any sense in updating everything through one function?
Only if you need to change lots of stuff at once. It's wasteful to include data in a frequent message that never changes and doesn't need to be sent.
It's perfectly fine to have different messages you send for different purposes and then the client has different listeners for those specific messages. At the same time, if you regularly send three pieces of data together, you probably wouldn't make a separate message for each piece of data - you'd put those three together such that your message structure aligns with your usage.
And, you can also have different messages for different purposes even if some data is in both messages.
One more note here. The title of your question "How should I manage the number of sockets in a node.js application?" seems to ask about managing the number of sockets. But, the rest of your question isn't about that at all. The rest of your question is about having different messages on the same socket. You don't need a new socket in order to define and use a different message. You can have thousands of different messages that you use all on the same socket connection. That's the whole architecture of socket.io. You send a message name and some data that goes with it. You can use a limitless number of separate message names all on the same connection.

How to model chat messages in an event-sourced system?

Context: I'm exploring to build an event sourced system / PoC using EventStoreDB (separate event stream per aggregate) with Node.JS/TypeScript. One part of the system is a 1:1 customer support chat. When a chat message is created, a push notification is sent to the user, including an update to the app's badge number (total unread message count). I'm wondering what's the best way to model the aggregates / bounded contexts.
Question 1: where to put the chat messages?
Question 2: how to handle a customer's unread message badge counter?
Since chat messages are by themselves already timed events, they seem like they could easily fit in an event sourced system. Still, I'm looking for advise on how to best model the aggregates:
Option A: Since each chat message has its own lifecycle (they can be edited, have a read status that gets updated, etc.), ChatMessage could be an aggregate on its own. This would explode the number of aggregates (and thus streams), but that might not really be such an issue for EventStoreDB. However, to send the notification for a message, we'll need to know the total number of unread messages (so info on other aggregates). But how should the push notification sending "saga" / "process manager" (which is the correct term?) know what badge counter to send with the notification? Should it keep its own state / read model with the current counter for each customer based on all the event it has seen?
Option B: Another way might be to have a list of messages under the Customer aggregate root. That way, Customer could have a counter for the number of unread messages and a fold of all the events would give me that number. However, here I'm afraid the large number of chat message events for the Customer aggregate root gets in the way of "simple" Customer behavior. E.g. when processing a Customer command, we'd first get the current state by folding all events (assume no snapshotting is used), which means applying all those chat events, even to just do something with the current name of the customer.
Option C: Or should these be in different bounded contexts? So have the Customer with it's contact details in a bounded context, and have a separate bounded context for chat (or communications in general), where both have a Customer aggregate root sharing only the UUID of the customer? Would that be best of both worlds, or would that give other challenges?
Is any of the options the way to go? Or is there another, better option? Or am I just missing the point entirely ;) (don't wanna rule that out)
Any advice is much appreciated!
Event Sourcing describes a way to (re)create state, by storing every change as an event. This does not include how those events get persisted or snapshotted, or how they are read and distributed.
I always start from the User Interface. Because that's where you should know which information you want to display and which actions can be executed.
For example there could be the following Commands (or actions executed by the User Interface):
SendMessage(receiverId, content)
MarkMessageAsRead(messageId)
Your server will then check if the provided data is valid and create the related Events:
class SupportChatMessageAggregate {
MessageId messageId;
UserId senderId;
UserId receiverId;
String content;
boolean readByReceiver;
// depending on framework and personal preference, this could
// also be a method: handle(SendMessage command, CurrentUser currentUser)
constructor(SendMessage command, CurrentUser currentUser) {
validate(command); // throws Exception if invalid
// for example if content is empty,
// or if currentUser is not allowed to send messages to receiverId
publishEvent(new MessageSentEvent(
command.getMessageId(),
currentUser.getUserId(),
command.getReceiverId(),
command.getContent()
));
}
handle(MarkMessageAsRead command, CurrentUser currentUser) {
validate(command); // throws Exception if invalid
// for example check if currentUser == receiver
publishEvent(new MessageMarkedAsReadEvent(
command.getMessageId(),
currentUser.getUserId()
));
}
...
}
Now when you want to know the badge counter for a User, you simply add up all the MessageSentEvents where receiver = currentUser, and subtract all the MessageMarkedAsReadEvents of the currentUser.
This could be done for example within the UnreadSupportChatMessageCountAggregate, that is responsible for providing the current unreadMessages value based on the MessageSentEvents and MessageMarkedAsReadEvents for a given User. A pretty boring Aggregate, but it does the job.
That's Event Sourcing: You simply have a bunch of events, and if you want to query some data, you just fetch all related events, process them, and get your result. If you use separate event streams per aggregate or just have a single stream for all events is an implementation detail (or depends on the event store you use).
Depending on the number of events this can be extremely fast, or very slow. That's where snapshots and/or read models (from CQRS) come in handy. But for plain Event Sourcing this is not required.

Dialogflow: Call third party api from webhook and wait for response

I am creating a chatbot which have an intent with a payment link. So on trigger of this intent, I made call from webhook fulfillment to third party api which takes approx 20secs to respond. But in this period of time my response is timed out as it is limited to 5 sec from google.
Can you please suggest what approach should I follow. I just want to wait for approx 20 sec to respond.
Thanks.
one option is to keep the conversation alive using events (generated by the webhook) which trigger dedicated intents.
When a payment must be performed the webhook starts a background process to deal with the 3rd party payment API, and sleep for 4-5 sec, after that generates an event (setFollowupEvent PAYMENT_IN_PROGRESS). This event is associated to a DialogFlow Intent which fires as soon as the event is sent back to the platform.
At this point you have another incoming webhook call: check status of the payment, if it is still in progress (likely after 5 sec) then sleep 4-5 sec and send another event (setFollowupEvent PAYMENT_IN_PROGRESS_2) which produces the same workflow.
There are so many times you can do this (I think a max of 3), so you need to cater for the fact that the payment does not terminate in time (fallback scenario).
A smart option could be to keep engaging the user with the conversation, not always easy, depending on what your chatbot is about.
Hope it helps.
The short answer is that you can't.
The longer answer is that you need to think about this as a conversation. If you asked someone a question, and didn't get any response from them for 20 seconds - that would be pretty uncomfortable, wouldn't it?
Instead, we have come up with ways to compensate for that silence. In a physical conversation, people may engage with you and ask you questions to fill the time. If you're on the phone, they may play hold music. Or we may end the conversation for now and tell them later when there is a result.
When building an Action, we have similar parallels that may work better or worse based on our exact needs.
Engaging in conversation
One approach is that when we get the request from the user, we do two things:
Start a task that will execute the query and save the results in a separate "answer database", indexed against the user, a session ID, or some other temporary id we can generate and use later.
While the query is running, we reply to the user saying we're working on it, and asking them another question.
Then, when they reply with their answer to this other question, we can check if we have an answer for them in the database. If we do, we'll reply with it. If not, we'll repeat step 2 until we do.
This approach works well if we either have other questions to ask, or if we're in a good place to "make small talk". Picture booking an airline reservation - while we look up flights, we may want to ask if they prefer window or aisle seats (Which we'd need to ask later) or make small talk by asking if they're traveling for business or leisure.
Using "hold music"
A variant of this allows us to play some hold music while we're processing the answer.
Instead of asking a question in step 2 above, we reply with a Media Response that plays 20 seconds or so of music. When the music completes, our fulfillment webhook will be sent a MEDIA_STATUS event and we can either return the information from the answer database, or say we're still working on it and play more music.
This is less conversational, but may work better if we don't actually have anything to say in the meantime.
Sending a notification
If the response may take a very long time, then it may just be best to let them do other things and to send a notification or a text or email when you have a result. These cases, however, require the user to have registered with you in some way and are probably more appropriate if you have a long-standing relationship with the user.
Summary
You should be returning results as quickly as you can to keep it feeling like a conversation. When you can't, consider other means, just like how we would consider what it would be like if we were talking to another person.

CQRS - applying command based on decision from multiple projections

Question is related to CQRS - I have user that wants to order something from web and is presented with GUI showing his balance = 100$ and stock = 1 item. Let's say we have 2 services here AccountService and StockService with separate concerns. In order to generate GUI for client third service AggregatorService listens to domain events from AccountService and StockService, projects a view and creates GUI for clients.
When user decides to order this item, he clicks a button and Command for order is sent to AccountService. Here we load AccountAggregate in order to decrease balance for the price of the item that needs to be ordered. But before I can do this, I have to check if the item is still available (or somehow to reserve it). Only thing that comes up to my mind is:
Read current stock of the item from read model of StockService, but what can happen is that other services read model is updated just a second after I read it (e.g. somebody bought the item, so actual stock is =0. but read model still has =1).
Before decreasing a balance call some method on StockService to reserve the item for some time. If order is not successful (e.g. no enough funds on balance, I would have to un-reserve it somehow). This needs to be some sync-REST call and it is probably slower than some async solution (if any).
Are there any best practices for this kind of use-case?
You have 2 options, depending on whether you embrace eventual consistency or not.
Using immediate consistency I would have an OrderService that receives the order request and it makes async calls to AccountService.ReservePayment() and StockService.ReserveStock(). If either of those fail you call AccountService.UndoReservePayment() and StockService.UndoReserveStock(). If both succeed you call AccountService.CompleteReservePayment() and StockService.CompleteReserveStock(). Make sure each reservation should have a timestamp on it so a daemon process can occasionally run and Undo any reserves that are older than an hour or so.
This approach makes the user wait until both those services complete. If either the StockService or the AccountService are slow, the user experience is slow. I suggest a better way to do this is the eventual consistency approach which gives the user a very fast experience at the expense of receiving failure messages after the fact.
With eventual consistency you assume they have enough credit and you have enough inventory, and in response to their order request you immediately send back a success message. The user is happy and they go along to buy more stuff.
The OrderCreated event is then handled asynchronously to reserve stock and credit as described above. However, since there is no time pressure to reply to the waiting user you don’t have to scale up to handle as high a throughput. If the credit check and stock check take a minute or two, the user doesn’t care because he’s off doing other things.
The price you pay here is that the user may get a success message at the time of order submission, then a few minutes later get an email saying the sale didn’t go through after all because there’s no stock. This is what many large retailers do, including Amazon, Target, Walmart, etc. Eventual consistency can go a long way towards easing the load and cost of the back end.

Resources