# Traffic Cloning to Multiple Elasticsearch Clusters with INFINI Gateway

> Clone traffic to multiple Elasticsearch clusters effortlessly with INFINI Gateway. Distribute requests simultaneously to separate clusters without code changes.

- Repository: [INFINI Labs/gateway](https://github.com/infinilabs/gateway)
- Tags: how-to-guide
- Published: 2026-03-04

---

**INFINI Gateway duplicates incoming requests across multiple independent processing flows using the clone routing filter, enabling simultaneous writes to separate Elasticsearch clusters without code modifications.**

The INFINI Gateway (`infinilabs/gateway`) provides a declarative mechanism to replicate traffic to multiple Elasticsearch clusters through its pipeline-based architecture. By combining the **clone** filter with **elasticsearch** output filters, operators can implement dual-write patterns, blue-green deployments, or multi-region replication purely through YAML configuration.

## Core Components of Traffic Cloning

### The Clone Routing Filter

The `clone` filter, implemented in [[`proxy/filters/routing/clone.go`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/clone.go)](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/clone.go), receives requests and sequentially invokes each configured downstream flow. At line 60 of the source, the `CloneFlowFilter.Filter` method calls `common.MustGetFlow(v)` for each flow name `v` listed in the configuration, then executes `flow.Process(ctx)` to dispatch the duplicated request into independent processing pipelines.

### The Elasticsearch Output Filter

Each downstream flow typically terminates with an **elasticsearch** output filter that forwards requests to a specific cluster. Connection parameters and cluster definitions are structured in [[`proxy/output/elastic/config.go`](https://github.com/infinilabs/gateway/blob/main/proxy/output/elastic/config.go)](https://github.com/infinilabs/gateway/blob/main/proxy/output/elastic/config.go), while the actual HTTP transmission logic resides in [`proxy/output/elastic/elasticsearch.go`](https://github.com/infinilabs/gateway/blob/main/proxy/output/elastic/elasticsearch.go).

## Configuration Steps for Multi-Cluster Setup

### Define Target Elasticsearch Clusters

Declare each target cluster in [`gateway.yml`](https://github.com/infinilabs/gateway/blob/main/gateway.yml) under the global `elasticsearch` section. Each entry requires a unique identifier, endpoint URL, and connection parameters:

```yaml
elasticsearch:
  es-us-east:
    elasticsearch: http://us-east.example.com:9200
    balancer: round_robin
    max_connection_per_node: 50
    timeout: 30s
  es-eu-west:
    elasticsearch: http://eu-west.example.com:9200
    balancer: round_robin
    max_connection_per_node: 50
    timeout: 30s

```

### Implement the Clone Flow

Create an entry flow that uses the `clone` filter to distribute traffic to multiple named flows. The `flows` array specifies which downstream flows receive the duplicated request:

```yaml
flow:
  - name: double_write
    filter:
      - clone:
          flows:
            - write_us_east
            - write_eu_west
          continue: false

```

### Configure Response Handling

The clone filter's `continue` parameter determines client response behavior. When set to `false` (default), the gateway finishes the request after all clones complete and returns the **last flow's response** to the client. Lines 63-65 of [`clone.go`](https://github.com/infinilabs/gateway/blob/main/clone.go) implement this logic: `if len(filter.Flows) > 0 && !filter.Continue { ctx.Finished() }`. Set `continue: true` to keep the original flow alive and return its response instead.

## Complete Configuration Example

The following [`gateway.yml`](https://github.com/infinilabs/gateway/blob/main/gateway.yml) clones write operations to two geographically separated clusters:

```yaml
elasticsearch:
  es-us-east:
    elasticsearch: http://us-east.example.com:9200
    balancer: round_robin
    max_connection_per_node: 50
    timeout: 30s
  es-eu-west:
    elasticsearch: http://eu-west.example.com:9200
    balancer: round_robin
    max_connection_per_node: 50
    timeout: 30s

flow:
  - name: double_write
    filter:
      - clone:
          flows:
            - write_us_east
            - write_eu_west
          continue: false

  - name: write_us_east
    filter:
      - elasticsearch:
          elasticsearch: es-us-east

  - name: write_eu_west
    filter:
      - elasticsearch:
          elasticsearch: es-eu-west

```

## Execution Flow Deep Dive

When a client request hits the `double_write` flow, the following sequence occurs:

1. **Request Reception**: The gateway matches the incoming request to the `double_write` flow in [[`proxy/filters/routing/clone.go`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/clone.go)](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/clone.go).

2. **Flow Resolution**: The clone filter iterates over the `flows` array and calls `common.MustGetFlow("write_us_east")` and `common.MustGetFlow("write_eu_west")` to resolve the downstream pipeline references.

3. **Sequential Processing**: For each resolved flow, the gateway invokes `flow.Process(ctx)`, which executes the elasticsearch output filter defined in the downstream configuration.

4. **Cluster Dispatch**: Each elasticsearch output filter forwards the request to its configured cluster using the connection pool established by the `ProxyConfig` struct in [[`proxy/output/elastic/config.go`](https://github.com/infinilabs/gateway/blob/main/proxy/output/elastic/config.go)](https://github.com/infinilabs/gateway/blob/main/proxy/output/elastic/config.go).

5. **Completion Check**: After both flows finish, the `continue: false` setting triggers `ctx.Finished()`, returning the response from `write_eu_west` (the last flow in the array) to the client.

## Summary

- **Clone filter location**: [[`proxy/filters/routing/clone.go`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/clone.go)](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/clone.go) implements the core duplication logic through `CloneFlowFilter.Filter` at line 60.
- **Zero-code implementation**: Traffic cloning requires only YAML configuration changes in [`gateway.yml`](https://github.com/infinilabs/gateway/blob/main/gateway.yml), with no modifications to the gateway source code.
- **Sequential execution**: Flows listed in the clone filter's `flows` array execute sequentially, not in parallel.
- **Response control**: The `continue` parameter determines whether the client receives the last clone flow's response (`false`) or the original flow continues processing (`true`).
- **Configuration reference**: Elasticsearch cluster definitions use the schema in [[`proxy/output/elastic/config.go`](https://github.com/infinilabs/gateway/blob/main/proxy/output/elastic/config.go)](https://github.com/infinilabs/gateway/blob/main/proxy/output/elastic/config.go).

## Frequently Asked Questions

### What happens if one Elasticsearch cluster fails during cloning?

If a downstream flow fails (e.g., network timeout to `es-eu-west`), the clone filter continues processing the remaining flows in the sequence. The gateway does not automatically rollback writes to clusters that succeeded earlier in the chain. For critical dual-write scenarios, implement health checks and circuit breakers in the elasticsearch output filter configuration to prevent partial write states.

### Can I clone traffic to more than two clusters?

Yes. The `flows` array in the clone filter accepts any number of flow names. Add additional entries to the array (e.g., `write_apac`, `write_backup`) and define corresponding downstream flows with elasticsearch output filters pointing to additional clusters defined in the `elasticsearch` section of [`gateway.yml`](https://github.com/infinilabs/gateway/blob/main/gateway.yml).

### How does the continue parameter affect client responses?

When `continue: false` (default), the gateway marks the request as finished after all clones complete and returns the response from the **last flow** in the `flows` array. When `continue: true`, the original flow resumes after dispatching clones, allowing subsequent filters to process the original response and return it to the client instead.

### Where is the clone filter documented?

User-facing documentation for the clone filter parameters and additional examples are available in the repository at [[`docs/content.en/docs/references/filters/clone.md`](https://github.com/infinilabs/gateway/blob/main/docs/content.en/docs/references/filters/clone.md)](https://github.com/infinilabs/gateway/blob/main/docs/content.en/docs/references/filters/clone.md). This documentation covers advanced options including conditional cloning and header manipulation during duplication.