Inter-Process communication in a microservices architecture - web-services

we are moving from monolithic to microservice architecture application, we're still in planning phase and we want to know what is the best practices of building it.
suppose we have two services :
User
Device
getUserDevices(UserId)
addDevice(DeviceInfo, UserId)
...
Each user has multiple devices
what is the most common, cleaner and proper way of asking the server to get all user devices ?
1- {api-url}/User/{UserId}/devices
needs another HTTP request to communicate with Device service.
for user X, get linked devices from User service.
// OR
2- {api-url}/Device/{UserId}/devices
for user X, get linked devices from Device service.

There are a lot of classic patterns available to solve such problems in Microservices. You have 2 microservices - 1 for User (Microservice A) and 1 for Device (Microservice B). The fundamental principle of a microservice is to have a separate database for each of the microservice. If any microservice wants to talk to each other (or to get data from another microservice), they can but they would do it using an API. Another way for communication between 2 microservices is by events. When something happens in Microservice A, it will raise an event and push it to a central event store or a message queue and Microservice B would subscribe to some or all of the events emitted by A.
I guess in your domain, A would have methods like - Add/Update/Delete a User and B would have Add/Update/Delete a device. Each user can have its own unique id and other data fields like Name, Address, Email etc. Each device can have its own unique id, a user id, and other data fields like Name, Type, Manufacturer, Price etc. Whenever you "Add" a device, you can send a POST request or a command (if you use CQRS) to Device Microservice with the request containing data about device + user-id and it could raise an event called "DeviceAdded". It can also have events corresponding to Update and Delete like "DeviceUpdated" and "DeviceRemoved". The microservice A can subscribe to events - "DeviceAdded", "DeviceRemoved", and "DeviceUpdated" events emitted by B and whenever any such event is raised, it will handle that event and denormalize that event into its own little database of Devices (Which you can call UserRelationships). In future, it can listen to events from other microservices too (so your pattern here would be extensible and scalable).
So now to get all devices owned by a user, all you have to do is make an end-point in User Microservice like "http://{microservice-A-host}:{port}/user/{user-id}/devices" and it will return you a list of the devices by querying for user-id in its own little database of UserRelationships which you must have been maintaining through events.
Good Reference is here: https://www.nginx.com/blog/event-driven-data-management-microservices/

it may really be either way, but to my liking, I would choose to put it under /Devices/{userId}/devices as you are looking for the devices given the user id. I hope this helps. Have a nice one!

You are requesting a resource from a service, resource being a device and service being a device service.
From a rest standpoint, you are looking for a resource and your service is providing various methods to manipulate that resource.
The following url can be used.
[GET] ../device?user_id=xyz
And device information can be fetched via ../device/{device_id}
Having said that, if you had one service that is providing for both user and device data than the following would have made sense.
[GET] ../user/{userId}/device
Do note that this is just a naming convention and you can pick what suits best for you, thing is pick one and hold onto it.
When exposing the api consistency is more important.

One core principle of the microservice architecture is
defining clear boundaries and responsibilities of each microservice.
I can say that it's the same Single Responsibility Principle from SOLID, but on macro level.
Сonsidering this principle we get:
Users service is responsible for user management/operations
Devices service is responsible for operations with devices
You question is
..proper way of asking the server to get all user devices
It's 100% responsibility of the Devices service and Users service nothing know about devices.
As I can see you thinking only in routing terms (yes API consistency is also important).
From one side the better and more logical URL is /api/users/{userId}/devices
- you try to get user's devices, these devices belong to user.
From other side you can use the routes like /api/devices/user/{userId} (/api/devices/{deviceId}) and that can be more easily processed
by the routing system to send a request to the Devices service.
Taking into account other constraints you can choose the option that is right for your design.
And also small addition to:
needs another HTTP request to communicate with Device service.
in the architecture of your solution you can create an additional special and separate component that routes the requests to the desired microservice, not only direct calls are possible from one microservice to another.

You should query the device service only.
And treat the user id like a filter in the device service. For eg: you should search on userid similar to how you would search device based on device type. Just another filter
Eg : /devices?userid=
Also you could cache some basic information of user in device service, to save round trips on getting user data

With microservices there is nothing wrong with both the options. However the device api makes more sense and further I'll prefer
GET ../device/{userId}/devices
over
GET ../device?user_id=123
There are two reasons:
As userId should already be there with devices service you'll save one call to user service. Otherwise it'll go like Requester -> User service -> Device Service
You can use POST ../device/{userId}/devices to create new device for particular user. Which looks more restful then parameterized URL.

Related

Amazon Connect Stop Call Recording

Is it possible to stop call recordings in Amazon Connect so the customer and agent can discuss sensitive material without being recorded?
I am aware of the set call recording behaviour blocks, but they don't seem to work on a call that has already been started with an agent with call recording enabled. Transferring to another contact flow with the recording type set to none doesn't seem to make a difference and the call carries on being recorded.
I am aware of the sample workflow Sample secure input with agent as outlined in this AWS blog https://aws.amazon.com/premiumsupport/knowledge-center/disable-recording-amazon-connect. This does work, however it relies on the customer entering payment details whilst the agent is on hold - preventing the agent and customer from having a sensitive conversation.
It seems the only way to stop recording once it has been enabled is to put the agent on hold?
Do not know if you have not solved your issue yet, but amazon has update their Amazon Connect API that would allow you to suspend the recording.
Boto3 implementation
response = client.suspend_contact_recording(
InstanceId='string',
ContactId='string',
InitialContactId='string'
)
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/connect.html#Connect.Client.suspend_contact_recording
They have also allow you to Start, Pause, Stop. (
We have just started to review this for a POC, turn recording off be default for a group of queues. Allow to Agents to start and stop and pause recording as needed.
You can also read this in an Amazon Blog post that should be able to help you fully implement the solution.
https://aws.amazon.com/blogs/contact-center/pausing-and-resuming-call-recordings-with-a-new-api-in-amazon-connect/#:~:text=is%20not%20recorded.-,End%20the%20call.,you%20start%20and%20stop%20it.
After speaking with Architects at AWS, the desired and designed for solution is to have the customer automatically enter sensitive information with the agent on hold and call recording turned off to remain PCI compliant.
If that is not an option there are workarounds possible that go against the way Amazon Connect has been designed. In order to turn off call recording once it has been enabled on a call, a new contact ID must be established. To do this you would need to transfer the user to your external phone number again or transfer to a queue and disable call recording in that new flow.
This brings in extra issues around how to get the customer back to the original agent once the sensitive information has been discussed. It also means you would potentially have 3+ contact IDs for the same transaction, with call recording spread across them.

Communicate internally between Google Cloud Functions?

We've created a Google Cloud Function that is essentially an internal API. Is there any way that other internal Google Cloud Functions can talk to the API function without exposing a HTTP endpoint for that function?
We've looked at PubSub but as far as we can see, you can send a request (per say!) but you can't receive a response.
Ideally, we don't want to expose a HTTP endpoint due to the extra security ramifications and we are trying to follow a microservice approach so every function is its own entity.
I sympathize with your microservices approach and trying to keep your services independent. You can accomplish this without opening all your functions to HTTP. Chris Richardson describes a similar case on his excellent website microservices.io:
You have applied the Database per Service pattern. Each service has
its own database. Some business transactions, however, span multiple
services so you need a mechanism to ensure data consistency across
services. For example, lets imagine that you are building an e-commerce store
where customers have a credit limit. The application must ensure that
a new order will not exceed the customer’s credit limit. Since Orders
and Customers are in different databases the application cannot simply
use a local ACID transaction.
He then goes on:
An e-commerce application that uses this approach would create an
order using a choreography-based saga that consists of the following
steps:
The Order Service creates an Order in a pending state and publishes an OrderCreated event.
The Customer Service receives the event attempts to reserve credit for that Order. It publishes either a Credit Reserved event or a
CreditLimitExceeded event.
The Order Service receives the event and changes the state of the order to either approved or cancelled.
Basically, instead of a direct function call that returns a value synchronously, the first microservice sends an asynchronous "request event" to the second microservice which issues a "response event" that the first service picks up. You would use Cloud PubSub to send and receive the messages.
You can read more about this under the Saga pattern on his website.
The most straightforward thing to do is wrap your API up into a regular function or object, and deploy that extra code along with each function that needs to use it. You may even wish to fully modularize the code, as you would expect from an npm module.

strongloop/loopback - Change connection string based on route value

My application's users are geographically dispersed and data is stored in various regions. Each region has it's own data center and database server.
I would like to include a route value to indicate the region that the user wants to access and connect to, as follows:
/api/region/1/locations/
/api/region/2/locations/
/api/region/3/locations/
Depending on the region passed in, I would like to change the connection string being used. I assume this can be performed somewhere in the middleware chain, but don't know where/how. Any help is appreciated!
What should not be done
Loopback provides a method MyModel.attachTo (doesnt seem to be documented, but a reference to it is made there ).
But since it is a static method, it affects the entire Model, not a single instance.
So for this to work on a per-request basis, you must switch the DB right before the call to the datasource method, to make sure nothing async starts in between. I don't think this is possible.
This is an example using an operation hook (and define all datasources, include dbRegion1 below in datasources.json)
Bad, don't that below. Just for reference
Region.observe('loaded', function filterProperties(ctx, next) {
app.models.Region.attachTo(app.dataSources.dbRegion1);
}
But then you will most likely face concurrency issues when your API receives multiple requests in a short time.
(Another way to see it is that the server is no longer truly stateless, execution will not depend only on inputs but also on a shared state).
The hook may set region2 for request 2 while the method called after the hook was expecting to use region1 for request 1. This will be the case if something async is triggered between the hook and the actual call to the datasource method.
So ultimately, I don't think you should do that. I'm just putting it there because some people have recommended it in other SO posts, but it's just bad.
Potential option 1
Build an external re-routing server, that will re-route the requests from the API server to the appropriate region database.
Use the loopback-connector-rest in your API server to consume this microservice, and use it as a single datasource for all your models. This provides abstraction over database selection.
Then of course there is still the matter of implementing the microservice, but maybe you can find some other ORM than loopback's that will support database sharding, and use it in that microservice.
Potential option 2
Create a custom loopback connector that will act as router for MySQL queries. Depending on region value passed inside the query, re-route the query to the appropriate DB.
Option 3
Use a more distributed architecture.
Write a region-specific server to persist region-specific data.
Run for instance 3 different servers, each one configured for a region.
+ 1 common server for routing
Then build a routing middleware for your single user-facing REST api server.
Basic example:
var express = require('express');
var request = require('request');
var ips = ['127.0.0.1', '127.0.0.2'];
app.all('/api/region/:id', function (req, res, next) {
console.log('Reroute to region server ' + req.params.id);
request(ips[req.params.id], function (error, response, body) {
if (err) return next(err);
next(null, body);
});
});
Maybe this option is the easiest to do

Selecting message queue approach for multiple consumers in AWS

Please help selecting a MQ app/system/approach for the following use-case:
Check for incoming messages for a specific user -> read the message if available -> delete from the queue, ideally, staying within AWS.
Context:
Social networking app, users receiving messages, i.e.
I need to identify incoming messages by recipient ID.
The app is doing long-polls for new messages every 30 seconds.
Message size is <1Kb.
As per current estimates, I'll need 100M+ message checks per months in total (however, much less messages, these are just checks).
While users acknowledge messages choosing OK or Ignore, however not sure if ACK support is required from MQ system for that.
I'm in AWS. Initially thought of SQS, but the more I read the less it looks like a good match - cannot set message recipient ID in a way to filter by recipient, etc, however maybe I'm wrong.
One of the options I also thought about is to just use DynamoDB's "messages" table, partition key being userId and sort key being a messageId, thus I'll be able to easily query by a user, however concerned with costs.
If possible, I would much more prefer to stay within AWS or at least use SAAS like SQS, as being a 1-person startup I really want to avoid headaches supporting self-hosted system.
Thank you!
D
You are right on both these counts:
SQS won't work, because of the limitation you pointed.
DynamoDB would work, but cost a lot.
I can suggest the following:
Create a Redis cluster, possibly on Amazon ElastiCache.
In it, make one List per user.
Whenever a new message comes, append it to concerned User's list.
To deliver the message, just read from the User's list. Also, flush the queue if needed.
What I am suggesting is very similar to how Twitter manages each User's news-feed and home-feed.
It should also be cheap.

How to manage Akka Actor's paths in distributed system?

Suppose I have a the following two Actors
Store
Product
Every Store can have multiple Products and I want to dynamically split the Store into StoreA and StoreB on high traffic on multiple machines. The splitting of Store will also split the Products evenly between StoreA and StoreB.
My question is: what are the best practices of knowing where to send all the future BuyProduct requests to (StoreA or StoreB) after the split ? The reason I'm asking this is because if a request to buy ProductA is received I want to send it to the right store which already has that Product's state in memory.
Solution: The only solution I can think of is to store the path of each Product Map[productId:Long, storePath:String] in a ProductsPathActor every time a new Product is created and for every BuyProduct request I will query the ProductPathActor which will return the correct Store's path and then send the BuyProduct request to that Store ?
Is there another way of managing this in Akka or is my solution correct ?
One good way to do this is with Akka Cluster Sharding. From the docs:
Cluster sharding is useful when you need to distribute actors across
several nodes in the cluster and want to be able to interact with them
using their logical identifier, but without having to care about their
physical location in the cluster, which might also change over time.
There is an Activator Template that demonstrates it here.
To your problem, the concept of StoreA and StoreB are each a ShardRegion and map 1:1 with to your cluster nodes. The ShardCoordinator manages distribution between these nodes and acts as the conduit between regions.
For it's part, your Request Handler talks to a ShardRegion, which routes the message if necessary in conjunction with the coordinator. Presumably, there is a JVM-local ShardRegion for each Request Handler to talk to, but there's no reason that it could not be a remote actor.
When there is a change in the number of nodes, ShardCoordinator needs to move shards (i.e. the collections of entities that were managed by that ShardRegion) that are going to shut down in a process called "rebalancing". During that period, the entities within those shards are unavailable, but the messages to those entities will be buffered until they are available again. To this end, "being available" means that the new ShardRegion responds to a directed message for that entity.
It's up to you to bring that entity back to life on the new node. Akka Persistence makes this very easy, but requires you to use the Event Sourcing pattern in the process. This isn't a bad thing, as it can lead to web-scale performance much more easily. This is especially true when the database in use is something like Apache Cassandra. You will see that nodes are "passivated", which is essentially just caching off to disk so they can be restored on request, and Akka Persistence works with that passivation to transparently restore the nodes under the control of the new ShardRegion – essentially a "move".