Waldemar's Blog

Ambassador Pattern

Understanding the Ambassador Pattern for Distributed Systems

Distributed systems can offer a range of benefits, including improved scalability, better fault tolerance, and increased flexibility. However, designing and managing distributed systems can be challenging, particularly when it comes to ensuring that services can communicate with one another effectively. This is where the Ambassador Pattern comes in.

What is the Ambassador Pattern?

The Ambassador Pattern is a design pattern that provides a decoupling mechanism between services in a distributed system. This pattern is particularly useful in cases where a service needs to access another service that is running on a different network and is not directly accessible.

The Ambassador Pattern involves placing an intermediary service (the ambassador) between the service that needs to access a remote service and the remote service itself. The ambassador acts as a proxy for the remote service and is responsible for handling the communication between the two services.

How does the Ambassador Pattern work?

By using the Ambassador Pattern, the service that needs to access the remote service can do so without having to know the details of the remote service's location or network configuration.

When a service needs to access a remote service using the Ambassador Pattern, it sends a request to the ambassador service. The ambassador service then sends the request on to the remote service and returns the response to the calling service. The calling service is unaware that the ambassador service has been used to facilitate communication.

The way I see it, in simplified terms, the Ambassador Pattern is there to maintain the contract between two services

How does this play with the Sidecar Pattern?

Previously we spoke about the Sidecar Pattern. I did some more research into it and found that they compliment each other fairly directly, we essentially call it the Sidecar-Ambassador.

Default Ambassador

A simplified Ambassador Pattern would look something like this:

Ambassador Pattern

(I’ve started using PlantUML for my diagrams because the idea of designs as code just excites me)

In this design, we can see the client communicating with the services via the Ambassadors. The contract is fulfilled by the ambassador. Seems like a redundancy if you’re thinking too 2-dimensionally, we have to bear in mind that not all services communicate similarly, it could be via API, RPC, or even something like a simple TCP request.

Ambassador Sidecar

Let’s forget about the Client now and we consider service-to-service communication. Imagine we have a service that utilises logging-as-a-service (LAAS) to publish its logs. The sidecar knows to publish all logs to the filesystem but it also knows that Error and Critical logs get published to LAAS for later querying.

Let’s look at some facts:

  1. The Service doesn’t care what happens to its logs, it just has a package installed (sidecar) that gobbles them up.
  2. The Sidecar knows to split up the logs based on severity but it only cares about publishing severe logs to a message bus.
  3. The Ambassador knows to consume logs from that message bus and submit it to LAAS via a TCP endpoint.

If LAAS ever changes, we’ll have to update our Ambassadors to look at the new one via whatever method is required but the Sidecar and most of all the Service don’t care in the slightest.

Ambassador - Sidecar Pattern

(Still getting the hang of PlantUML!)

It should be noted that this is how I envision using it, it’s not necessarily a strict reference to the actual pattern. Perhaps I’m even implementing it incorrectly, I’m happy to hear feedback.

Ultimately I’m only implementing here on a small scale. The reality is that the Ambassadors can be used to facilitate critical things like load balancing or error handling itself.

Conclusion

The Ambassador Pattern is a useful design pattern for distributed systems that can help to reduce coupling between services and improve the overall scalability and flexibility of a system. By using an intermediary service to facilitate communication between services, the Ambassador Pattern makes it easier to manage and scale distributed systems, particularly in microservice architectures.

Update!

After a discussion with a brilliant colleague of mine, I realised that the Ambassador doesn’t always act as a proxy. Here’s a quick and simple example of our regular use-case Ambassador:

Ambassador as proxy

Simple enough, right? We fire off a request, we get a response. What the Ambassador does is its own business, as long as we get the response, as we would from a proxy. This could be a REST call and we’ll be waiting for a 2xx response but if we get a different response we can take logic to action around that.

What if we don’t care about the response? What if it’s a fire-and-forget call? This kind of makes sense, considering the Ambassador will usually have logic to facilitate things like circuit-breaking, in which case waiting for a response could become a lengthy affair. Instead of a request, we’re sending an action. A “do this, I don’t care about what happens” instead of “do this, I’ll wait to hear how it went.”

Ambassador without proxy

In these cases, an Ambassador no longer acts as a proxy. We’re sending a request and trusting it to take care of the rest. That means that if it fails in the request, it should be prepared to do the logging and handling of those failures.

#coding #computerScience #designPatterns