46

I am relatively new to microservice architecture. We have a moderately sized web application and I am weighing the pros and cons of breaking it out into microservices instead of a monolithic system we have now moving forward.

As far as I understand it, consider microservices A and B each of which rely on a subset of data that the other has. If a message is posted by A saying that something has changed, B can consume that message and replicate a local copy of A's info and use that to do whatever B needs to do.

However, what if B goes down/fails and after a while, comes back up again. During that down time, A has published two more messages. How does B know how to update its local copy of A's info?

Granted, if B is the only consumer of A's queue, then it can start reading it once it comes back online but what if there are other consumers of that queue and those messages are consumed?

As a more concrete example, if a Users service has its email address updated while a Billing microservice is down, if the Billing microservice comes back up again, how does it know that the email has been updated?

When microservices come back up, does it do a broadcast saying "Hey I'm back up, give me all your current info?"

In general what would be the best industry practices for data synchronization?

6
  • 5
    To avoid it whenever possible.
    – Telastyn
    Jul 10, 2018 at 20:59
  • 1
    Why does Orders need to know anything about Users?
    – kdgregory
    Jul 10, 2018 at 21:01
  • 7
    It's just an example. Replace the two with whatever you want that makes sense.
    – noblerare
    Jul 11, 2018 at 3:39
  • a fan out routing will solve your 'message is consumed by someone else' problem. but its really unclear what you are trying to achieve.
    – Ewan
    Jul 11, 2018 at 9:57
  • @Ewan I've updated my original post to better explain what I'm trying to ask.
    – noblerare
    Jul 11, 2018 at 12:45

4 Answers 4

21

After doing a bit more research, I stumbled upon this article from which I've pulled some quotes out that I think is helpful for what I want to accomplish (and for any future readers). This offers a way to adopt a reactive programming model over an imperative programming model.

Event-sourcing

The idea here is to represent every application’s state transition in a form of an immutable event. Events are then stored in a log or journal form as they occur (also referred to as ‘event store’). They can also be queried and stored indefinitely, aiming to represent how the application’s state, as a whole, evolved over time.

What this helps accomplish is that if a microservice goes down yet other events pertinent to it are being published and events are consumed by, say, other instances of that microservice, when that microservice comes back up, it can refer to this event store to retrieve all the events that it missed during the period it went down.

Apache Kafka as Event Broker

Consider the use of Apache Kafka which can store and dispatch thousands of events per second and has built-in replication and fault-tolerance mechanisms. It has a persistent store of events which can be stored on disk indefinitely and consumed at any time (but not removed) from the Topic (Kafka's fancy queue) were delivered to.

The events are then assigned offsets that univocally identify them within the Topic — Kafka can manage the offsets itself, easily providing “at most once” or “at least once” delivery semantics, but they can also be negotiated when an event consumer joins a Topic, allowing microservices to start consuming events from any arbitrary place in time — usually from where the consumer left off. If the last consumed event offset is transactionally persisted in the services’s local storage when the usecases ‘successfully complete’, that offset can easily be used to achieve an “exactly once” event delivery semantics.

In fact, when consumers identify themselves to Kafka, Kafka will record which messages were delivered to which consumer so that it doesn't serve it up again.

Sagas

For more complex usecases where the communication among different services is indeed necessary, the responsibility of finishing the usecase must be well recognized — the usecase is decentralized and only finishes when all the services involved acknowledge their task as successfully completed, otherwise the whole usecase must fail and corrective measures must be triggered to rollback any invalid local state.

This is when saga comes into play. A saga is a sequence of local transactions. Each local transaction updates the database and publishes a message or event to trigger the next local transaction in the saga. If a local transaction fails because it violates a business rule then the saga executes a series of compensating transactions that undo the changes that were made by the preceding local transactions. Read this for more info.

3
  • I still do not understand why you want to build such a complicated structure. It is usually much easier if each service just holds its own data and gives it to other services upon request. Jul 12, 2018 at 18:38
  • 4
    ^But it will reduce availability of the system. The complicated structure might be warranted if high resilience is required.
    – avmohan
    Feb 7, 2019 at 7:43
  • 1
    @JFabianMeier Little you know about microservices. One issue with obtaining data via (http) requests on demand is, that there may be failures (connection gets lost, service down or under high load, resulting in slow or no responses at all). This can be partly compensated with caching. But where you really get issues is, when you the data is required for sorting and filtering. Its not possible to perform this on database and the only wait for proper sorting and filtering semantics would be to load everything in memory and filter there, which is awfully slow and requires a lot of resources.
    – Tseng
    Aug 20, 2023 at 14:13
5

Even if I'm late, I would to put my 2 cents on the argument because I think it's an important point when you want to evaluate e design an event-driven microservices architecture. Each microservice knows exactly which are the events that impact on its state and is able to wait for them. When the microservice is not avaliable, there should be a component that keeps messages that are needed from the failed microservice until it is not able to "consume" them. This is in fact a "producer/consumer" model and not a "publish/subscribe" one. Message brokers (like Kafka, RabbitMQ, ActiveMQ etc. ) are usually the best way to achieve this behaviour (unless you are not implementing something different such as event sourcing) providing persistent queues and ack/nack mechanism.

Now the microservice know that a message is eventually delivered but it is not enough: which is the way it expects the delivery of a single message? can it manage the delivery of multiple copies of the same event notification? This is matter of delivery semantic (at least once, exactly once)

Final thought(s):

  1. When you add a microservice to your architecture that needs to consume events from others, you have to do the first sync

  2. Even the broker can fail, in this case messages are lost

for both scenarios, it would be useful to have simple mechanisms to re-hydrate you microservice state. It could be A REST API or a script that sends messsages, but the most important thing is to have means to do some maintenance task

1
  • Most important note here is, that Kafka is not a message broker. It do not provide (yet) all the properties of a queue to be defined as such. Kafka is an event streaming system, which is completely different form a message broker. Kafka also do not track the stage of the consumers or its logs/tops (it doesnt have queues), the clients have to do that instead.
    – Tseng
    Aug 20, 2023 at 14:15
0

You can replace a normal event queue with a publisher/subscriber model, where A service publish a new message of topic T and B type of microservices would subscribe to the same topic.

Ideally B would be a stateless service, and it would utilize a detached persistence service, such that a failed B service instance would be replaced by spawning one or more B service instances to continue its work, reading from the same shared persistence service.

0

If a message is posted by A saying that something has changed, B can consume that message and replicate a local copy of A's info and use that to do whatever B needs to do.

If you wanted B to be able to access A's internal data, you would be better of to just give it access to A's internal databases.

However you should not do that, the whole point of a service oriented architecture is that service B cannot see service A's internal state and is limited to make requests through REST APIs (and vice versa).

In your case you could have a user data service, which has the responsibility of storing all user data. Other services that want to use that data only request it when they need it and don't keep a local copy (which btw. is really useful if you think about GDPR compliance). The User Data service can support simple CRUD operations like "Create new user", or "Change name for user_id 23" or it can have more complex operations, "Find all standard users with a birthday coming up in the next 2 weeks and give them premium trial status". Now when your Billing service needs to send an email to user 42, it will ask the User Data service "What is the email address for user_id 42", use its internal data with all the billing information to craft the email and then may pass the email address and body to a mail server.

1
  • 1
    This beats the purpose of having microservices in the first place, because the database would be the bottleneck. Also it violates the single-responsibility and source of truth principles applied to Microservices, which mean only one service should have ownership of the data (read: be allowed to write and update the data)
    – Tseng
    Aug 20, 2023 at 14:16

Not the answer you're looking for? Browse other questions tagged or ask your own question.