# Scalable data services

With Discovery’s scalable data services, you don’t need to limit a [data service](../datasource/datasource-data-services.md) to a static list of providing peers. Instead, you can define a data service’s providers using a regular expression, which allows the data service to recognise new instances of providing peers added at runtime.

## Requirements

To take advantage of scalable data services, you need to have already implemented:

* Discovery licensing
* Peer discovery

## Overview

In a traditional data service, providing peers are defined as a static list of labels (`remote-label`). Any peer added at runtime that is not in the static list is not recognised by the data service as as a provider.

**Defining providing peers by static labels**

Consider the data service below:

```
add-data-service
    …
    add-source-group
        add-priority
            remote-label [blue]##trading-adapter1##
            remote-label [blue]##trading-adapter2##
            remote-label [blue]##trading-adapter3##
        end-priority
    end-source-group
end-data-service
```

This data service recognises providing peers defined at configuration time: trading-adapter1, trading-adapter2, and trading-adapter3. At runtime, if a new instance of the trading adapter (trading-adapter4) is added by Discovery’s peer discovery, then the data service will not recognise it as a provider.

In a _scalable_ data service, providing peers are defined as a label pattern (`remote-label-regex`). The pattern (a regular expression) is evaluated at runtime for each subscription request, and any peer added at runtime with a label that matches the pattern is recognised by the data service as a provider.

**Defining providing peers by label patterns (regular expressions)**

Consider the data service below:

```
add-data-service
    …
    add-source-group
        add-priority
            remote-label-regex [blue]##^trading-adapter[0-9]+##
        end-priority
    end-source-group
end-data-service
```

This data service recognises providing peers with labels that match the pattern `^trading-adapter[0-9]+` (a label that starts with `trading-adapter` followed by one or more digits). At runtime, if a new instance of the trading-adapter (trading-adapter4) is added by Discovery’s peer discovery, then the data service will recognise it as a provider.

## Common configurations

In a data service’s configuration, providing peers are divided by source group then by priority (primary, secondary, ...):

```plantuml
hide empty members
entity "Data service" as dataservice
entity "Source group" as sourcegroup
entity "Priority" as priority
entity "Providing peer" as peer

dataservice -right-|{ sourcegroup
sourcegroup -right-|{ priority
priority -right-|{ peer

note bottom of peer
Requests are load-balanced
across peers.
end note

note bottom of priority
Priorities are defined in
descending order of priority;
primary priority first.

Requests are routed to the
highest priority that has at
least one available peer.
end note

note bottom of sourcegroup
Requests are routed to
each source group.
end note
```

The most common arrangement of providing peers for a scalable data service is the [Load-balanced arrangement](#load-balanced-arrangement). Other arrangements are possible, however. This section describes three common arrangements of providing peers.

### Load-balanced arrangement

Providing peers are arranged in a single-priority source group.

In the Liberator configuration below, requests for subjects beginning `/MyNamespace` are distributed to the providing peer serving the least number of subscriptions to the data service.

This configuration is commonly used for deployments hosted on a container-orchestration platform, such as Kubernetes. 

```plantuml
component Liberator {
    component MyDataService <<data service>> {

    }
}
component Adapter1
component Adapter2
component Adapter3

MyDataService --> Adapter1
MyDataService --> Adapter2
MyDataService --> Adapter3
```

|     |     |
| --- | --- |
```
add-data-service
    service-name MyDataService
    include-pattern ^/MyNamespace/.+
    add-source-group
        add-priority
            remote-label Adapter1
            remote-label Adapter2
            remote-label Adapter3
        end-priority 
    end-source-group
end-data-service
```
| .Traditional configuration | .Scalable configuration |
```
add-data-service
    service-name MyDataService
    include-pattern ^/MyNamespace/.+
    add-source-group
        add-priority
            remote-label-regex ^Adapter[0-9]+$
        end-priority 
    end-source-group
end-data-service
```

### Failover arrangement

Providing peers are arranged in a multi-priority source group.

In the configuration below, requests for subjects beginning `/MyNamespace` are load balanced across the primary priority adapters.  If _all_ the primary priority adapters fail, then Liberator moves existing requests and routes new requests to the secondary priority adapters.

This configuration is common in deployments that have two software stacks ('legs'), with each stack hosted on separate hardware for resilience. This arrangement is less appropriate for container-orchestrated platforms, such as Kubernetes, which run on clusters of worker nodes and are more resilient by design.

```plantuml
component Liberator {
    component MyDataService <<data service>> {

    }
}

folder "Primary priority" {
component Adapter103
component Adapter102
component Adapter101
}

folder "Secondary priority" {
component Adapter203
component Adapter202
component Adapter201
}

MyDataService --> Adapter101
MyDataService --> Adapter102
MyDataService --> Adapter103
MyDataService -[hidden]- Adapter201
MyDataService -[hidden]- Adapter202
MyDataService -[hidden]- Adapter203
```

|     |     |
| --- | --- |
```
add-data-service
    service-name MyDataService
    include-pattern ^/MyNamespace/.+
    add-source-group
        add-priority ①
            remote-label Adapter101
            remote-label Adapter102
            remote-label Adapter103
        end-priority
        add-priority ②
            remote-label Adapter201
            remote-label Adapter202
            remote-label Adapter203
        end-priority
    end-source-group
end-data-service
```
| .Traditional configuration &lt;1> Primary priority &lt;2> Secondary priority | .Scalable configuration |
```
add-data-service
    service-name MyDataService
    include-pattern ^/MyNamespace/.+
    add-source-group
        add-priority ①
            remote-label-regex ^Adapter1[0-9]+$
        end-priority
        add-priority ②
            remote-label-regex ^Adapter2[0-9]+$
        end-priority
    end-source-group
end-data-service
` &lt;1> Primary priority &lt;2> Secondary priority |

### Parallel arrangement

Providing peers are arranged in multiple source groups.

In the configuration below, requests for subjects beginning `/MyNamespace` are routed to both a load-balanced set of AdapterA instances and to a load-balanced set of AdapterB instances. Liberator combines the data received from both requests

```plantuml
component Liberator {
    component MyDataService <<data service>> {

    }
}

folder "Source Group 1" {
component AdapterA3
component AdapterA2
component AdapterA1
}

folder "Source Group 2" {
component AdapterB3
component AdapterB2
component AdapterB1
}

MyDataService --> AdapterA1
MyDataService --> AdapterA2
MyDataService --> AdapterA3
MyDataService --> AdapterB1
MyDataService --> AdapterB2
MyDataService --> AdapterB3
```

|     |     |
| --- | --- |
```
add-data-service
    service-name MyDataService
    include-pattern ^/MyNamespace/.+
    add-source-group
        add-priority
            remote-label AdapterA1
            remote-label AdapterA2
            remote-label AdapterA3
        end-priority
    end-source-group
    add-source group
        add-priority
            remote-label AdapterB1
            remote-label AdapterB2
            remote-label AdapterB3
        end-priority
    end-source-group
end-data-service
```
| .Traditional configuration | .Scalable configuration |
```
add-data-service
    service-name MyDataService
    include-pattern ^/MyNamespace/.+
    add-source-group
        add-priority
            remote-label-regex ^AdapterA[0-9]+$
        end-priority
    end-source-group
    add-source-group
        add-priority
            remote-label-regex ^AdapterB[0-9]+$
        end-priority
    end-source-group
end-data-service
```

## Source affinity

[Source affinity](../datasource/datasource-load-balance-adapters-using-source-affinity.md) is supported by scalable data services. When all of a data service’s peers are defined by `remote-label-regex`, affinity key values are cached centrally with Discovery. The central cache guarantees that all components use the same affinity key values when routing requests to providing peers.

Once an providing peer has been cached under an affinity key, Discovery retains the key until the peer disconnects from Discovery.

**Scalable data service with source affinity**

A data service for a trade channel, /PRIVATE/TRADE, routes requests to a single-priority source group of trading adapter instances. The source group has an affinity key defined with the format: `trading-adapter-__session_id__`.

```
object-map /PRIVATE/TRADE /PRIVATE/[blue]##%U##/TRADE ①

add-data-service
    service-name trade-channel
    include-pattern ^/PRIVATE/[blue]##[^/]+##/TRADE
    add-source-group
        affinity trading-adapter ^/PRIVATE/[blue]##([^/]+)##/TRADE ②
        add-priority
            remote-label-regex ^trading-adapter[0-9]+$
        end-priority 
    end-source-group
end-data-service
```
1. Trade channel subject mapped to `/PRIVATE/#__session_id__#/TRADE`
2. Affinity key: `trading-adapter-#__session_id__#`

## Service rebalancing

Scalable data services support rebalancing of workloads within source groups that do not have source affinity enabled (see `add-source-group` configuration option [`affinity`](../datasource/datasource-data-services-configuration.md#affinity-2)).

Service rebalancing is disabled for all data services by default. To enable service rebalancing for all data services, set the configuration item [`service-rebalance-enable`](../datasource/datasource-data-services-configuration.md#service-rebalance-enable). To enable service rebalancing for a specific data service, set the data service’s configuration option [`rebalance-enable`](../datasource/datasource-data-services-configuration.md#rebalance-enable).

In source groups _without_ source affinity, rebalancing occurs when a connected peer that matches a priority’s `remote-label-regex` changes its status to UP. The source group discards all existing subscriptions and re-requests them, balancing the requests evenly over peers in the source group’s highest available priority band.

For more information, see:

* [`service-rebalance-enable`](../datasource/datasource-data-services-configuration.md#service-rebalance-enable)
* [`service-rebalance-time`](../datasource/datasource-data-services-configuration.md#service-rebalance-time)
* `add-data-service:` [`rebalance-enable`](../datasource/datasource-data-services-configuration.md#rebalance-enable)
* `add-data-service:` [`rebalance-time`](../datasource/datasource-data-services-configuration.md#rebalance-time)

---

**See also:**

* [Migrate to scalable data services](discovery-migrating-scalable-data-services.md)
