Руководство прикладного разработчика#

В данном руководстве приведены инструкции по подключению и использованию компонента Сессионные данные (SUSD) продукта Platform V SessionsData (SUS).

Для приложений master-ССД и manager-ССД, входящих в состав компонента Сессионные данные, данное руководство неприменимо.

Этот документ содержит названия переменных, которые применимы для различных сред контейнеризации, указанных в системных требованиях руководства по установке.

Термины и сокращения#

Термин или сокращение

Описание или расшифровка

АРМ

Автоматизированное рабочее место

Клиент

Программный компонент вычислительной системы, посылающий запросы серверу

КМ

Клиентский модуль

Кеш

Промежуточный буфер с быстрым доступом к нему, содержащий информацию, которая может быть запрошена с наибольшей вероятностью

НТ

Нагрузочное тестирование

СД

Сессионные данные — горячий кеш для оперативных и справочно-конфигурационных данных в привязке к клиенту

ССД

Сервис сессионных данных, компонент Сессионные данные

IAGW

Компонент Внутренний шлюз ЕФС

k8s

Kubernetes

Kafka

Apache Kafka

Manager-ССД

Приложение ufs-session-manager компонента Сессионные данные

Master-ССД

Приложение ufs-session-master компонента Сессионные данные

One Nio сериализатор

Библиотека odnoklassniki/one-nio, преобразующая Java-объекты в собственный бинарный формат

Pod

Группа из одного или нескольких контейнеров с общим хранилищем и сетевыми ресурсами, а также спецификацией для запуска

SBS

Spring Boot Starter

Servant-ССД

Приложение ufs-session-servant компонента Сессионные данные

Sidecar

Контейнер, который запущен рядом с основным контейнером внутри pod

Slave-ССД

Приложение ufs-session-slave компонента Сессионные данные

Системные требования#

Список программного обеспечения, поддерживаемого программным компонентом, смотрите в документе «Руководство по установке», раздел «Системные требования».

Подключение и конфигурирование#

Подключение и настройка клиентского модуля ССД#

Актуальные версии артефактов для подключения, конфигурирования и использования ССД представлены в таблице ниже.

Идентификатор группы

Артефакт

Версия

{домен}.ufs.sds

sds-impl

7.3.3.2

{домен}.ufs.sds

sds-spring-boot-autoconfigure

7.3.3.2

{домен}.ufs.sds

sds-spring-boot-starter

7.3.3.2

{домен}.ufs.sds

sds-api

7.3.3

ru.odnoklassniki

one-nio

1.0.2-sbtfix10

Подключение и настройка КМ ССД через Spring Boot Starter#

SBS ССД является оберткой над КМ ССД, упрощающей его использование в Spring Boot приложениях потребителей ССД.

1. Подключите Maven-зависимости SBS ССД

<!-- Spring Boot Starter компонента Сессионные данные -->
<dependency>
   <groupId>{домен}.ufs.sds</groupId>
   <artifactId>sds-spring-boot-starter</artifactId>
   <version><!-- из таблицы с актуальными версиями артефактов выше  --></version>
</dependency>

<!-- Spring Boot Starter Технологического ядра платформы -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>environment-spring-boot-starter</artifactId>
    <version>7.3.0.3</version>
</dependency>

<!-- Библиотека кеша Технологического ядра платформы -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>ufs-platform-cache-impl</artifactId>
    <version>7.1.1.0</version>
</dependency>

<!-- Spring Boot Starter HTTPClient -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>httpclient-spring-boot-starter</artifactId>
    <version>7.3.5.0</version>
</dependency>

КМ ССД подключается транзитивно через зависимость sds-spring-boot-starter.

2. Настройте КМ ССД через SBS ССД

Настройка SBS ССД выполняется в файле application.yaml Spring Boot приложений потребителей ССД.

При необходимости можно указывать специфичные для окружения настройки в дополнительных файлах в application-DEV.yaml и application-PROM.yaml.

REMOTE-режим:

ufs:
  sds:
    client:
      mode: REMOTE                   # Признак того, что данные хранятся в распределенном кластере ССД и передаются по сети (стандартный промышленный режим)
      http:
        integration-enabled: true    # Признак, информирующий SBS ССД о том, что приложение обслуживает бизнес-запросы по протоколу HTTP (SBS ССД получит доступ ко всем HTTP-запросам и ответам)

LOCAL-режим:

ufs:
  sds:
    client:
      mode: LOCAL                    # Признак того, что данные хранятся в локальном кеше (JSR107) в памяти приложения
      http:
        integration-enabled: true    # Признак, информирующий SBS ССД о том, что приложение обслуживает бизнес-запросы по протоколу HTTP (SBS ССД получит доступ ко всем HTTP-запросам и ответам)

Примечание. Потребителям LOCAL-режима клиентского модуля ССД следует понимать, что сессионные данные хранятся в локальном кеше их приложения и не используются совместно разными сервисами.

Режим может быть удобен для АРМ компонентов, которые сами создают сессию (standalone режим), необходимую им только ради кеша, а не ради обмена информацией с другими компонентами.

Кроме того, на такой локальной сессии можно запустить простой процесс Workflow, не имеющий external-переходов в другие сервисы.

Подключение и настройка КМ ССД через xml-конфигурацию Spring#

Для Java Enterprise/WAR-проектов, не использующих Spring Boot, следует подключать КМ ССД непосредственно и явно его настраивать.

1. Подключите Maven-зависимости КМ ССД

<!-- Клиентский модуль компонента Сессионные данные -->
<dependency>
    <groupId>{домен}.ufs.sds</groupId>
    <artifactId>sds-impl</artifactId>
    <version><!-- из таблицы с актуальными версиями артефактов выше  --></version>
</dependency>

<!-- Библиотека сериализации/десериализации (пакет one.nio.serial), fork из https://github.com/odnoklassniki/one-nio -->
<dependency>
    <groupId>ru.odnoklassniki</groupId>
    <artifactId>one-nio</artifactId>
    <version><!-- из таблицы с актуальными версиями артефактов выше  --></version>
</dependency>

<!-- Технологическое ядро платформы -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>ufs-platform-core</artifactId>
    <version>7.3.0.3</version>
</dependency>

<!-- Защита от внутренних отказов ЕФС -->
<dependency>
    <groupId>{домен}.ufs.healthcheck</groupId>
    <artifactId>healthcheck-control</artifactId>
    <version>7.0.3.10</version>
</dependency>

<!-- Библиотека Технологического ядра платформы для работы со Spring -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>ufs-platform-spring</artifactId>
    <version>7.0.1.0</version>
</dependency>

<!-- Библиотека Технологического ядра платформы для работы с JSON -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>ufs-platform-json-mapper</artifactId>
    <version>7.0.3.0</version>
</dependency>

<!-- Библиотека кеша Технологического ядра платформы -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>ufs-platform-cache-spring</artifactId>
    <version>7.1.1.0</version>
</dependency>

<!-- HTTPClient -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>ufs-platform-httpclient-spring</artifactId>
    <version>7.3.5.0</version>
</dependency>

<!-- Клиентский модуль компонента Журналирование (LOGA) продукта Platform V Monitor (OPM) -->
<dependency>
    <groupId>{домен}.ufs.platform</groupId>
    <artifactId>logger-impl</artifactId>
    <version><!-- из документации компонента Журналирование (LOGA) продукта Platform V Monitor (OPM) --></version>
</dependency>

2. Настройте КМ ССД через xml-конфигурацию Spring

Если прикладному сервису необходимо читать/записывать сессионные данные, то в Spring-контексте приложения в платформенном теге <core:environment> укажите источник сессионных данных:

<beans ...
    xmlns:core="http://ufs.{домен}/platform/core"
    ...
    xsi:schemaLocation="http://ufs.{домен}/platform/core http://ufs.{домен}/platform/core/core-module.xsd"/><core:environment>
        ...
        <core:session source="sessionSource" healthCheckEnabler="..."/>  <!-- источник СД + выключатель TechnicalHealthCheck КМ ССД -->
    </core:environment>

Важно! Если прикладной сервис не намерен использовать сессионные данные, то источник СД указывать не нужно и тег <core:session> можно не указывать.

Атрибут healthCheckEnabler необязательный: по умолчанию TechnicalHealthCheck КМ ССД включен (он имеет имя {домен}.ufs.sds.impl.SdsClientHealthCheck в запросе /healthcheck/detail и выполняет проверку доступности slave-ССД, развернутого на localhost). В атрибуте healthCheckEnabler можно указать ссылку на bean типа com.google.common.base.Predicate<Void>, реализовав который в прикладном коде сервиса-потребителя, можно временно выключать TechnicalHealthCheck КМ ССД. Предикат вызывается при каждой попытке вызова TechnicalHealthCheck КМ ССД. Например, можно выключить HealthCheck ССД на 1 минуту после старта приложения, обеспечив тем самым прохождение прикладных Smoke-тестов при развертывании с помощью Deploy tools (CDJE) продукта Platform V DevOps Tools (DOT).

2.1. Выполните xml-настройку КМ ССД в REMOTE-режиме

Для использования распределенного кластера ССД с обменом сессионным данными по сети в качестве sessionSource используется экземпляр REST-клиента {домен}.ufs.platform.httpclient.JsonRestClient, сконфигурированный должным образом.

Bean sessionSource следует создавать кастомным тегом клиентского модуля ufs-platform-httpclient-spring.

<beans ...
    xmlns:httpclient="http://ufs.{домен}/platform/httpclient"
    ...
    xsi:schemaLocation="http://ufs.{домен}/platform/httpclient http://ufs.{домен}/platform/httpclient.xsd"/>

    <httpclient:rest-client id="sdsSource"
                            serviceCode="sds.slave"
                            configService="configService"/>

Примечание. Потребителям клиентского модуля ССД необязательно дословно копировать указанное выше описание JsonRestClient, к нему можно добавлять мониторинг, beforeHandlers, afterHandlers и прочие дополнительные возможности JsonRestClient, необходимые конкретному потребителю КМ ССД.

В таблице ниже указаны параметры, которые могут использоваться при конфигурации JsonRestClient.

Ключ

Описание

Тип

Обязательность

ufs.baseurl.sds.slave

Базовый URL для доступа к slave-ССД. Не используется в КМ ССД!

STRING

+

ufs.httpclient.connections.max.total.sds.slave

Максимальное количество соединений от КМ ССД к slave-ССД

LONG

+

ufs.httpclient.connections.max.per-route.sds.slave

Максимальное количество соединений от КМ ССД к slave-ССД на узел

LONG

+

ufs.httpclient.time-out.request.milliseconds.sds.slave

Таймаут запросов от КМ ССД к slave-ССД (в миллисекундах)

LONG

+

ufs.httpclient.time-out.connection.milliseconds.sds.slave

Таймаут на соединение от КМ ССД к slave-ССД (в миллисекундах)

LONG

+

ufs.httpclient.headers.name.server-ip.sds.slave

Конфигурация передачи IP-адреса сервера в заголовке от КМ ССД к slave-ССД

STRING

+

ufs.httpclient.circuit-breaker.enable.sds.slave

Признак работы механизма circuit breaker

BOOLEAN

+

ufs.httpclient.circuit-breaker.failure-rate.threshold.percent.sds.slave

Максимальный процент ошибок в потоке запросов, после превышения которого circuit breaker перейдет в режим OPEN - поток запросов будет прерван

LONG

+

ufs.httpclient.circuit-breaker.state.closed.request-buffer.size.request-count.sds.slave

Размер буфера запросов для режима CLOSED, после заполнения которого рассчитывается процент ошибок в потоке запросов

LONG

+

ufs.httpclient.circuit-breaker.state.half-open.request-buffer.size.request-count.sds.slave

Размер буфера запросов для режима HALF_OPEN, после заполнения которого рассчитывается процент ошибок в потоке запросов

LONG

+

ufs.httpclient.circuit-breaker.state.open.duration.milliseconds.sds.slave

Длительность режима OPEN механизма circuit breaker - режим, в котором поток запросов прерван (в миллисекундах)

LONG

+

ufs.httpclient.circuit-breaker.record.exceptions.sds.slave

Список исключений, которые считаются ошибкой и влияют на механизм circuit breaker

STRING

+

ufs.httpclient.circuit-breaker.record.httpException.codes.sds.slave

Список HTTP-кодов для исключений, которые считаются ошибкой для механизма circuit breaker

STRING

+

ufs.httpclient.circuit-breaker.smooth-start.rate-limit.upper.requests-per-second.sds.slave

Предел ограничения скорости запросов, при достижении которого задача плавного старта завершается

LONG

+

ufs.httpclient.circuit-breaker.smooth-start.rate-limit.start.percent.sds.slave

Ограничение скорости запросов в момент запуска задачи плавного старта. Указывается в процентах от предела ограничения скорости запросов

LONG

+

ufs.httpclient.circuit-breaker.smooth-start.rate-limit.change-step.percent.sds.slave

Шаг изменения ограничения скорости запросов в процессе работы задачи плавного старта. Указывается в процентах от предела ограничения скорости запросов

LONG

+

Примечание. Вышеперечисленные настройки содержатся в файле parameters.json для КМ ССД. Для того чтобы эти настройки попали в дистрибутив прикладного сервиса, нужно подключить Maven-плагин (ufs-artifact-maven-plugin), настроенный по инструкции разработчиков компонента PACMAN (CFGA) продукта Platform V Frontend Std (#FS). Прикладному проекту необходимо включать эти настройки в свои файлы конфигурации только в случае, если его значения указанных настроек отличаются от значений по умолчанию, заданных в КМ ССД.

Настройка КМ ССД через Java API#

Клиентский модуль ССД можно сконфигурировать из Java кода. Пример конфигурации клиентского модуля ССД в формате unit-теста:

public class SdsClientTest {

    private PlatformEnvironment platformEnvironment; // Платформенный контекст
    private SomeSupReader sup;                       // Какой-то читатель значений параметров
    private final Set<String> activeSpringProfiles = // Активные Spring-профили текущего приложения
            ImmutableSet.of( "DEV" );                // org.springframework.core.env.Environment
    private SessionDataService sds;                  // компонент Сессионные данные
    // ...

    @BeforeClass
    public void sdsConfiguration() {
        // ...
        // Получение реализации ССД, используя Service Provider Interface (SPI). Если в classpath
        // отсутствует реализация КМ ССД (подключен только API), то будет брошен SdsClientException.
        sds = SdsFacade.getSessionDataService();
        // Конфигурация ССД в LOCAL-режиме
        configureSds( sds, createLocalCache() );
    }

    private Cache<Object, Object> createLocalCache() {
        return new ConcurrentMapCacheManager()
                       .createConcreteCache( "sessionCache",
                                             new SimpleConfiguration( ImmutableMap.of() ) );
    }

    /**
     * Для конфигурации используются либо 6, либо 7 настроек, передаваемых в метод
     * SessionDataService#configure() в виде Map. В будущих версиях реализации (но не API!) КМ ССД
     * набор настроек может поменяться (с обратной совместимостью).
     */
    private void configureSds( SessionDataService theSds, Object sessionSource ) {
        ProductContext productContext = platformEnvironment.getProductContext();
        Map<String, Object> envParameters = new HashMap<>();
        envParameters.put( "subsystem.code", productContext.getSubsystemCode() );
        envParameters.put( "subsystem.channel", productContext.getSubsystemChannel() );
        envParameters.put( "deployment.unit", productContext.getDeploymentUnit() );
        envParameters.put( "application.active.profiles", activeSpringProfiles );
        if ( sessionSource instanceof Cache ) {
            envParameters.put( "data.source.type", "Local_CACHE" );
            envParameters.put( "cache.for.local.storage", sessionSource );
        } else if ( sessionSource instanceof JsonRestClient ) {
            envParameters.put( "data.source.type", "REMOTE_SERVICE" );
            envParameters.put( "rest.client.to.remote.service", sessionSource );
            envParameters.put( "rest.url.to.remote.service", supReader.getSdsUrl() );
        } else
            throw new IllegalArgumentException(
                    "Data source type must be either javax.cache.Cache or JsonRestClient" );

        theSds.configure( envParameters );
    }
    // ...

Начинайте взаимодействие с ССД с класса SdsFacade, из которого могут быть созданы/получены все необходимые объекты.

Важно! Если сторонний сервис не намеревался использовать сессионные данные и поэтому не подключал клиентский модуль ССД (ufs-platform-session-impl) в maven-зависимости, то вызов SdsFacade.getSessionDataService() не найдет в classpath настоящую реализацию ССД, вернув при этом заглушку. Эта заглушка отложено вернет SdsClientException уже при обращении к СД, если оно произойдет.

Полученный из SdsFacade объект класса SessionDataService (singleton) перед первым использованием нужно сконфигурировать методом configure().

В таблице ниже указаны параметры, которые используются при настройке ССД через Java API.

Ключ

Описание

Тип

Обязательность

ufs.sds.rest.url

URL для доступа клиентского модуля ССД к slave-ССД. Обратившись по данному URL к slave-ССД, клиентский модуль получит информацию о том, каким образом следует взаимодействовать с этим slave-ССД и по какому рабочему URL

STRING

+

Настройки, используемые при создании JsonRestClient, были описаны выше.

Примечание. Вышеперечисленные настройки содержатся в файле parameters.json для КМ. Чтобы эти настройки попали в дистрибутив прикладного сервиса, нужно подключить Maven-плагин (ufs-artifact-maven-plugin), настроенный по инструкции разработчиков компонента PACMAN продукта Platform V Frontend Std (#FS). Прикладному проекту необходимо включать эти настройки в свои файлы конфигурации только в случае, если его значения указанных настроек отличаются от значений по умолчанию, заданных в КМ ССД.

Подключение и конфигурирование sidecar slave-ССД#

Актуальная версия для подключения и конфигурирования sidecar slave-ССД представлена в таблице ниже.

Версия дистрибутива (бинарные артефакты)

Hash версии

D-10.011.00-3209

sha256:578c41789c4abcb2f5a65738b2c8f7b5ab56603ef35978167c213acba82fe04a

Чтобы подключить sidecar slave-ССД в прикладной проект, следуйте рекомендациям ниже.

1. Добавьте конфигурационные файлы

В каталог дистрибутива package/conf/config/parameters добавьте файл <your-fpi-name>.sds-slave-sidecar.all.conf (вместо <your-fpi-name> укажите fpi-name вашей подсистемы) со следующим содержимым:

# ПАРАМЕТРЫ ДЛЯ YAML-КОНФИГУРАЦИИ SIDECAR SLAVE-ССД
# Параметры Startup пробы
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.initialDelaySeconds=0
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.periodSeconds=2
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.timeoutSeconds=1
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.successThreshold=1
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.failureThreshold=15
# Параметры Liveness пробы
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.initialDelaySeconds=180
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.periodSeconds=5
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.timeoutSeconds=1
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.successThreshold=1
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.failureThreshold=3
# Параметры Readiness пробы
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.initialDelaySeconds=10
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.periodSeconds=1
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.timeoutSeconds=4
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.successThreshold=1
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.failureThreshold=3
# Значения запросов
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.requests.cpu=800m
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.requests.memory=1Gi
# Значения лимитов
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.limits.cpu=800m
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.limits.memory=1Gi
sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.limits.ephemeral-storage={{ lookup('custom_vars','global.ose.deployment.spec.template.spec.containers.resources.limits.ephemeral-storage',default='2Gi') }}

# Параметры приложения
# Параметры JAVA_OPTS
sds_slave_java_opts=-XX:+AlwaysActAsServerClassMachine -XX:ThreadStackSize=256 -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0

## Параметр старта sidecar slave-ССД -XX:+AlwaysActAsServerClassMachine требует, чтобы JVM в контейнере запускалась в серверном режиме.
## В первую очередь, это означает использование garbage collector G1.
## Если не задать этот параметр, то на небольших лимитах для контейнера slave-ССД (меньше 2-х CPU или 3-х ГБ RAM) JVM будет запускаться в клиентском режиме, в котором используется устаревший SerialGC,
## хуже справляющийся с очисткой мусора, чем G1.
## Параметр -XX:ThreadStackSize=256 задается для ограничения размера стека (stack) для каждого потока. Эта «квота», умноженная на размер пула потоков (thread pool),
## позволяет оценить общие расходы памяти JVM на стек.
## Для сервисов продукта хватает заданных по этой рекомендации 256 КБ. Если в конкретном сервисе-потребителе ССД по каким-либо причинам потребуется больше стека,
## то в конфигурационных файлах этого сервиса достаточно увеличить значение -XX:ThreadStackSize.

# Размер пула потоков
sds-slave.thread-pool.size=80
# Настройки Web-сервера
sds-slave.server.tomcat.max-threads=200
sds-slave.server.tomcat.min-spare-threads=10
sds-slave.server.max-http-header-size={{ lookup('custom_vars','global.server.max-http-header-size',default='32768') }}
# Подсистема владелец slave-ССД
sds-slave.owner.id=
# Значение graceful shutdown
sds-slave.graceful.shutdown.seconds=12

# Параметры компонента Журналирование
# Kafka
ufs-logger.kafka.bootstrap.servers={{ lookup('custom_vars','global.platform.logger.kafka.bootstrap.servers') }}
ufs-logger.kafka.ssl.enabled.protocols={{ lookup('custom_vars','global.platform.kafka.ssl.enabled.protocols') }}
ufs-logger.kafka.security.protocol={{ lookup('custom_vars','global.platform.logger.kafka.security.protocol') }}
ufs-logger.kafka.topic={{ lookup('custom_vars','global.platform.logger.kafka.topic') }}
# Parameters
ufs-logger.parameters.url={{ lookup('custom_vars','global.ose.platform.ufs.baseurl.logger') }}/ufs-logger-parameters-ng/parameters

Примечание. В конфигурационном файле указаны конкретные значения:

  • ресурсов контейнера slave-ССД (cpu/memory),

  • параметров readiness/liveness probes (зависят от значений ресурсов),

  • JVM аргументов sidecar slave-ССД (sds_slave_java_opts), от них зависит то, какой объем памяти потребляет sidecar,

  • пула потоков (thread pool) Web сервера (tomcat.max-threads).

Однако необходимо уточнить их во время НТ прикладного проекта, т.к. конкретные значения зависят от характера использования ССД в проекте (сохраняемых в ССД Java-объектов, размера этих данных и интенсивности чтения/записи этих данных во время обработки бизнес-запросов).

Если полученные во время НТ данные отличаются от указанных, то при изменении ресурсов контейнера slave-ССД (cpu/memory) необходимо установить синхронные значения для запросов (requests) и лимитов (limits).

В каталог дистрибутива package/conf/data/sup2/parameters/ добавьте файл ufs-session-slave.conf со следующим содержимым:

ufsparams_module_tenant_code={{ TENANT_CODE }}
ufsparams_module_monitoring_enabled=false
healthcheck.cascadeprotection.undefined=-1
healthcheck.cascadeprotection.serviceunavailable=503
ufsparams_agent_baseUrl_insecure={{ lookup('custom_vars','global.ose.params.baseUrl.stringToBind') }}paramsv2/
ufsparams_agent_baseUrl_secure={{ lookup('custom_vars','global.params.baseUrl.stringToBind') }}paramsv2/
ufsparams_agent_delay=10000
ufsparams_agent_max_file_size=104857600
ufsparams_agent_min_file_size=50
ufsparams_agent_ignore_file_pattern=glob:**sup2_common.json
ufsparams_agent_httpclient_circuitBreaker_enable=false
ufsparams_agent_httpclient_circuitBreaker_failureRate_threshold=50
ufsparams_agent_httpclient_circuitBreaker_openState_duration=60000
ufsparams_agent_httpclient_circuitBreaker_halfOpen_requestBuffer=0
ufsparams_agent_httpclient_circuitBreaker_minimumCalls_size=10
ufsparams_agent_startup_synchronization_timeout=3000
props.ufs.sds.master.balancer.deploy.unit.name=ufs-session-master
props.ufs.sds.direct.master.balancer.deploy.unit.name=ufs-session-master-direct
props.ufs.sds.one.nio.session.data.format=true
props.ufs.sds.local.sections.forbidden.by.default=false
props.ufs.sds.master.sync.base.url=!{{ lookup('custom_vars','global.ose.platform.ufs.baseurl.sds.master') }}
props.ufs.httpclient.connections.max.total.sds.master.healthcheck=3
props.ufs.httpclient.connections.max.per-route.sds.master.healthcheck=3
props.ufs.httpclient.time-out.request.milliseconds.sds.master.healthcheck=3000
application.deploymentUnit=ufs-session-slave
ufs_session_slave_sup2_scopeTemplate={{sup2_scopeTemplate}}

В каталог дистрибутива package/conf/k8s/base/<your-fpi-name>/configmaps/ (вместо <your-fpi-name> укажите fpi-name вашей подсистемы) добавьте файл sds-slave-config.yaml. Содержимое скопируйте из Приложения 1.

В каталог дистрибутива package/conf/k8s/base/<your-fpi-name>/configmaps/ (вместо <your-fpi-name> укажите fpi-name вашей подсистемы) добавьте файл sds-slave-logger-properties.yaml со следующим содержимым:

apiVersion: v1
kind: ConfigMap
metadata:
  name: <your-fpi-name>.sds-slave-logger-properties.${distrib.release.version}
data:
  slave-logger.properties: |-
    logger.local.enabled=true
    logger.local.stdout.appender.enabled=true
    logger.local.level=ERROR
    logger.local.filePath=logs/
    logger.local.fileMaxSize=100
    logger.local.zipFilePath=logs/
    logger.local.fileMaxHistory=60
    logger.local.totalSizeCap=20
    logger.remote.level=ERROR

Внимание! После копирования замените <your-fpi-name> в блоке metadata → name всех добавленных файлов в каталоге package/conf/k8s/base/<your-fpi-name>/configmaps/ на fpi-name вашей подсистемы.

2. Доработайте dc.yaml

Добавьте в блоке containers описание контейнера slave-ССД:

containers -> image
containers -> envFrom -> configMapRef -> name
containers -> envFrom -> secretRef -> name

Фрагмент из dc.yaml:

containers:
  - image: {{ registry }}{{ registry_path }}/susd/ufs-session-slave@sha256:578c41789c4abcb2f5a65738b2c8f7b5ab56603ef35978167c213acba82fe04a
    imagePullPolicy: IfNotPresent
    name: sds-slave
    env:
      - name: ufs.tenant.code
        value: '{{ TENANT_CODE }}'
      - name: ose.namespace
        valueFrom:
          fieldRef:
            fieldPath: metadata.namespace
    envFrom:
      - configMapRef:
          name: <your-fpi-name>.sds-slave-sidecar.all.conf.${distrib.release.version}
      - secretRef:
          name: secret-<your-fpi-name>.${distrib.release.version}
    ports:
      - containerPort: 9080
        protocol: TCP
    startupProbe:
      httpGet:
        path: /ufs-session-slave/rest/healthcheck/groups/startup
        port: 9080
        scheme: HTTP
      initialDelaySeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.initialDelaySeconds}
      periodSeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.periodSeconds}
      timeoutSeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.timeoutSeconds}
      successThreshold: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.successThreshold}
      failureThreshold: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.startupProbe.failureThreshold}
    livenessProbe:
      httpGet:
        path: /ufs-session-slave/rest/healthcheck/groups/liveness
        port: 9080
        scheme: HTTP
      initialDelaySeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.initialDelaySeconds}
      periodSeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.periodSeconds}
      timeoutSeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.timeoutSeconds}
      successThreshold: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.successThreshold}
      failureThreshold: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.livenessProbe.failureThreshold}
    readinessProbe:
      httpGet:
        path: /ufs-session-slave/rest/healthcheck/groups/readiness
        port: 9080
        scheme: HTTP
      initialDelaySeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.initialDelaySeconds}
      periodSeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.periodSeconds}
      timeoutSeconds: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.timeoutSeconds}
      successThreshold: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.successThreshold}
      failureThreshold: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.readinessProbe.failureThreshold}
    resources:
      limits:
        cpu: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.limits.cpu}
        memory: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.limits.memory}
        ephemeral-storage: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.limits.ephemeral-storage}
      requests:
        cpu: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.requests.cpu}
        memory: ${sds-slave.ose.deployment.spec.template.spec.containers.sds-slave.resources.requests.memory}
    terminationMessagePath: /dev/termination-log
    volumeMounts:
      - mountPath: /etc/config/ssl/
        name: ufs-mq-jks-vol
        readOnly: true
      - mountPath: /tmp/ufsparams/config
        name: sds-slave-config
      - mountPath: /tmp
        name: tmp-dir
      - mountPath: /tmp/ufs-logger
        name: sds-slave-logger-properties

Внимание! После копирования замените <your-fpi-name> на fpi-name вашей подсистемы.

Добавьте в блоке labels версию дистрибутива sidecar slave-ССД.

Фрагмент из dc.yaml:

spec:
    ...
    template:
        metadata:
            labels:
                ...
                sdsSlaveDistribVersion: D-10.011.00-3209

Добавьте в блоке volumes описание тома.

Фрагмент из dc.yaml:

volumes:
  - name: sds-slave-config
    configMap:
      name: <your-fpi-name>.sds-slave-config.${distrib.release.version}
  - name: tmp-dir
    emptyDir: {}
  - name: sds-slave-logger-properties
    configMap:
      defaultMode: 420
      name: <your-fpi-name>.sds-slave-logger-properties.${distrib.release.version}

Внимание! После копирования замените <your-fpi-name> на fpi-name вашей подсистемы.

3. Настройте Istio

3.1. Настройте Istio Ingress

Для оптимальной работы ССД требуется липкая сессия (sticky session) к узлам сервиса-потребителя. А именно: сессионные HTTP-запросы от одного и того же клиента сервиса должны приходить на один и тот же экземпляр приложения ССД.

Липкая сессия к pod настраивается с помощью DestinationRule в Ingress.

Конфигурация DestinationRule:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: ingressgateway-sticky-session-<fp-name>
spec:
  exportTo:
    - .
  host: <fp-service-name>
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpCookie:
          name: UFS-SESSION
          path: /sds-sticky-session/nonexistent-path
          ttl: 0s

Внимание! После копирования укажите вместо <fp-name> и <fp-service-name> название вашей подсистемы и название вашего сервиса соответственно.

3.2. Настройте Istio Egress

Поскольку sidecar slave-ССД взаимодействует с master-ССД, и это взаимодействие должно быть защищенным, то необходимо должным образом настроить Istio Egress, который будет перенаправлять все запросы к master-ССД по протоколу HTTPS.

Проекты подключаются к Istio mesh (Synapse).

Маршрутизация сетевого трафика настраивается с помощью конфигурационных файлов Istio.

Брокеры очередей и IAGW находятся вне кластера.

Чтобы иметь возможность взаимодействовать с данными компонентами, необходимо рассказать про них Istio.

Для этого в ServiceEntry перечисляются порты для kafka и хост/порт балансировщика IAGW.

Внутри mesh HTTP-трафик - незашифрованный, вся работа с TLS происходит на egress gateway.

Для того чтобы завернуть на egress все HTTP-запросы клиентских модулей, адресованные IAGW, необходимо использовать gateway и virtualService конфигурации, а в destination rules указать инструкции для шифрования исходящего трафика.

Подробно про маршрутизацию трафика в Istio можно прочитать:

  • в книге «Istio: приступаем к работе» Калькот Ли, Бутчер Зак.

  • Istio. Traffic Management

Быстрый старт#

Этапы взаимодействия с ССД:

  1. Подключение приложения. Инструкции по подключению смотрите в разделе «Подключение и конфигурирование».

  2. Использование приложения. Как использовать компонент Сессионные данные, читайте в разделе «Использование программного продукта».

  3. Проверка работоспособности. Как проверить функционирование приложений ССД, смотрите в пункте «Проверка работоспособности».

Проверка работоспособности#

Для проверки правильности функционирования slave-ССД в pod прикладного проекта выполните следующие действия:

  1. Проверьте, что проходит readiness-probe: в консоли зайдите в конкретный pod каждого приложения (Pods -> Pod Details), перейдите на вкладку Events. Если ошибок нет, то pod стартовал.

  2. Проверьте Status pod: в консоли откройте вкладку Pods. Нажмите на нужный pod, перейдите во вкладку YAML, найдите containerStatuses -> name: sds-slave. Должно быть указано: started:true, ready:true.

Для проверки правильности функционирования servant-ССД выполните специальные запросы:

  • http://<хост:порт>/ufs-session-servant/rest/environment/product. В ответе обратите внимание на параметры: success: true, в body «distribVersion» - текущая версия дистрибутива {distrib-version}.

  • http://<хост:порт>/ufs-session-servant/rest/healthcheck. В ответе обратите внимание на параметры: success: true, body: OK.

  • http://<хост:порт>/ufs-session-servant/rest/v1/info/getSDSInfo?needExtendedInfo=true.

В ответе должны быть поля:

{
    "success": true,
    "body": {
        …
        "threadPoolInJNDI": true,
        …
        "supParametersReadOK": true,
         …
        "httpclientSupParameters": { "ufs.baseurl.sds.* ": [прописан правильный адрес]}
    }
}

Запросы можно выполнить как в браузере, так и при помощи утилиты curl: curl -X GET <запрос>.

Использование программного компонента#

Использование клиентского модуля ССД#

Клиентский модуль ССД можно использовать из Java-кода.

Перед использованием SessionDataService сконфигурируйте клиентский модуль так, как описано в пункте «Настройка КМ ССД через Java API». Если КМ ССД уже подключен так, как описано в пункте «Подключение и настройка КМ ССД через Spring Boot Starter», то дополнительно конфигурировать его через Java API не требуется. Доступ к классу SessionDataService в этом случае можно получить через Spring bean {домен}.ufs.sds.api.SdsAccessor, вызвав метод SdsAccessor.get().

Ниже представлены примеры в форме unit-тестов, иллюстрирующие основные операции для работы с сессией в ССД (создание, поиск, сохранение, удаление и др.).

Создание сессии ССД#

public class SdsClientTest {
    // ...
    private SessionDataService sds;  // компонент Сессионные данные
    private String sessionId;

    @BeforeMethod
    public void createSdsSession() {
        SdsSession session = sds.newSession( getMetaInfoWithClientIdAndIp(), ImmutableMap.of() );
        this.sessionId = session.getId();
    }

    private SdsSessionMetaInfo getMetaInfoWithClientIdAndIp() {
        SdsSessionMetaInfo metaInfo = sds.createSessionMetaInfo();
        metaInfo.setAttribute( SessionMetaAttribute.CLIENT_ID, "Current client login" );
        metaInfo.setAttribute( SessionMetaAttribute.CLIENT_IP, "Current client IP address" );
        return metaInfo;
    }
    // ...

Примечание. В прикладных сервисах создание сессии в ССД требуется только в том случае, если для них это не делает компонент, создающий сессию (например, Стартовый Менеджер (SMGX) продукта Platform V Starting Manager (SMG).

Иллюстрация получения сессии по ID и изменения ее данных#

public class SdsClientTest {
    // ...
    private SessionDataService sds;  // компонент Сессионные данные
    private String sessionId;

    @Test
    public void getAndSaveSessionDataSample() {
        SdsSession session = sds.getSession( sessionId,
                                             sds.createSessionMetaInfo(), ImmutableMap.of() );
        // Манипуляции с данными секции sharedSection, для чего секция загрузится из ССД
        getAndSetAttributeOf( session.getSection( "SDS-DEMO-SUBSYSTEM", "sharedSection" ),
                              "sharedAttribute", 123 );
        // А в случае с metaInfo-секцией ее загрузка выполняется одновременно с getSession
        getAndSetAttributeOf( session.getMetaInfo(),
                              "metaAttribute", "45" );
        // Для того чтобы изменения вступили в силу, выполним сохранение данных
        sds.saveSessionSections( session, ImmutableMap.of() );
    }

    private <AttrType extends Serializable> void getAndSetAttributeOf( SdsSessionSection section,
                                                                       String attrName,
                                                                       AttrType attrValue ) {
        assertNull( section.getAttribute( attrName ) );
        section.setAttribute( attrName, attrValue );
        assertEquals( section.getAttribute( attrName ), attrValue );
    }
    // ...

При создании сессии, а также при ее поиске по sessionId, укажите в текущем запросе имена секций, которые необходимы для дальнейшей работы.

Иллюстрация работы с сессией с указанием секций, с которыми будет работать приложение#

Для оптимальной работы ССД крайне важно явно указывать секции сессии, с которыми будет работать приложение. Это позволяет избежать лишних запросов к ССД, т.к. указанные секции будут загружены вместе с сессией при первом обращении к ней. А значит, не нужно будет лишний раз отправлять запросы по сети, что значительно улучшает производительность приложения и уменьшает нагрузку на сеть. Существует несколько альтернативных способов указать намерение использовать определенные секции. Они описаны ниже.

По умолчанию вместе с сессией загружается только ее METAINFO-секция. Все остальные, в том числе local-секции, нужно указывать самостоятельно.

Указание секций с помощью аннотации @SectionsIntendedToUse#
@SdsSectionsIntendedToUse( url = "/some/path*", namespace = "someNamespace", sectionNames = "someSection" )
public class DemoEndpoint {
    //...
}

Аннотация @SdsSectionsIntendedToUse указывает КМ ССД, что при обращениях по адресам (url), соответствующим маске /some/path*, будет использоваться секция «someSection» из namespace «someNamespace». Параметры url и namespace не являются обязательными. Если не указан url, то в качестве маски будет использоваться /*, а если не указан параметр namespace, то будет использоваться namespace по умолчанию (в логах приложения можно увидеть его техническое название ${UNNAMED}).

Аннотацию @SdsSectionsIntendedToUse можно указать у любого Spring bean. В том числе у тех bean, которые помечены @Configuration, @SpringBootApplication и у методов-конструкторов с аннотацией @Bean.

Даже одной аннотации @SdsSectionsIntendedToUse может быть достаточно на все приложение. Ключевую роль играет именно маска url, которая передается аннотации в качестве параметра. При принятии решения о том, какие секции нужно запросить из back, КМ ССД будет сопоставлять url, на который пришел запрос, с маской. При этом сопоставляется суффикс url, начинающийся сразу после server.servlet.context-path. Например, если

server.servlet.context-path=/path/sample/

то для url:

http://some.host:8080/path/sample/controller-path1/do-something
http://some.host:8080/path/sample/controller-path2/do-something-else

можно указать параметр url равным /controller-path*. Если требуется распространить загрузку секций на все url приложения, укажите просто /* (или ничего не указывать, т.к. /* – это значение по умолчанию).

Указание секций с помощью интерфейса SdsSectionsIntendedToUseSupplier#
@Component
public class DemoSectionsIntendedToUseSupplier implements SdsSectionsIntendedToUseSupplier {

    private final Map<String, List<SdsSectionIntendedToUseId>> sectionsIntendedToUse = new HashMap<>();

    public void addSections( String url, List<SdsSectionIntendedToUseId> sections ) {
        sectionsIntendedToUse.put( url, sections );
    }

    @Override
    public Map<String, List<SdsSectionIntendedToUseId>> sectionsByUrls() {
        // Возвращаем набор секций, которые намереваемся использовать
        return sectionsIntendedToUse;
    }

    @Override
    public boolean callOnlyOnce() {
        // Обеспечиваем постоянные вызовы метода {@link #sectionsByUrls()}, чтобы наборы секций можно было менять
        return false;
    }
}

public class DemoEndpoint {

    //...

    private final DemoSectionsIntendedToUseSupplier sectionsIntendedToUseSupplier;

    public DemoEndpoint( /* ... */, DemoSectionsIntendedToUseSupplier sectionsIntendedToUseSupplier ) {
        //...
        this.sectionsIntendedToUseSupplier = sectionsIntendedToUseSupplier;
    }

    //...

    @GET
    @Path( "/addSectionIntendedToUse" )
    public String addSectionIntendedToUse( @QueryParam( "namespace" ) String namespace,
                                           @QueryParam( "name" ) String name )
    {
        sectionsIntendedToUseSupplier.addSections( "/*",
                                                   List.of( new SdsSectionIntendedToUseId( namespace, name ) ) );
        return String.format( "Секция '%s' из пространства имен '%s' добавлена в список секций, " +
                              "которые намереваетесь использовать.", name, namespace );
    }
}

public class DemoEndpointIT {

    //...

    @BeforeMethod
    public void beforeMethod() {
        // Добавляем секцию "section" из пространства имен "namespace" в набор секций, т.к. намереваемся использовать ее в
        // тестах. Это позволяет уменьшить нагрузку на сеть, потому что не будет происходить лишних REST-запросов к ССД.
        doGetRequest( "/addSectionIntendedToUse?namespace={1}&name={2}", "namespace", "section" );
        // Создаем новую сессию и сохраняем ее идентификатор для последующих запросов
        createSessionAndSaveSessionCookie();
    }

    //...

}

Интерфейс SdsSectionsIntendedToUseSupplier позволяет указать нужные секции ССД в привязке к маскам бизнес-адресов URL. Это более гибкий вариант аннотации @SdsSectionsIntendedToUse, т.к. позволяет вычислять наборы нужных секций динамически в runtime. В остальном назначение данного интерфейса идентично аннотации @SdsSectionsIntendedToUse. В примере определен класс DemoSectionsIntendedToUseSupplier, реализующий интерфейс SdsSectionsIntendedToUseSupplier. В тесте DemoEndpointIT с помощью метода /addSectionIntendedToUse из DemoEndpoint добавляется секция «section» из пространства имен «namespace» в список секций, которые планируется использовать.

Указание секций напрямую при использовании SessionDataService#
public class SdsClientTest {
    //...
    private SessionDataService sds;  // компонент Сессионные данные
    private String sessionId;

     /**
     * Работать с сессией, указав секции, которые намереваемся использовать - более эффективно.
     * ID сессии обычно распространяется между сервисами посредством сессионной cookie.
     */
    @Test
    public void getSessionWithSectionsIntendedToUseIsMoreEfficient() {
        List<Triple<String, String, SectionCharacteristic[]>> sectionsIntendedToUse =
                ImmutableList.of( createSectionId( "namespace", "section1" ),
                                  createSectionId( "namespace", "section2" ) );
        SdsSession session = sds.getSession( sessionId, sds.createSessionMetaInfo(),
                ImmutableMap.of( "sections.intended.to.use", sectionsIntendedToUse ) );

        // Манипуляции с данными section1, но ее загрузка уже произошла, т.к. выше указали
        // ССД, что намереваемся использовать эту секцию
        getAndSetAttributeOf( session.getSection( "namespace", "section1" ),
                              "attribute", "value1" );
        // Аналогично, section2 тоже уже загружена
        getAndSetAttributeOf( session.getSection( "namespace", "section2" ),
                              "attribute", "value2" );

        sds.saveSessionSections( session, ImmutableMap.of() );
    }

    private Triple<String, String, SectionCharacteristic[]> createSectionId(
            String namespace, String sectionName,
            SectionCharacteristic... characteristics ) {
        return Triple.of( namespace, sectionName, characteristics );
    }

    private <AttrType extends Serializable> void getAndSetAttributeOf( SdsSessionSection section,
                                                                       String attrName,
                                                                       AttrType attrValue ) {
        assertNull( section.getAttribute( attrName ) );
        section.setAttribute( attrName, attrValue );
        assertEquals( section.getAttribute( attrName ), attrValue );
    }
    //...

При поиске сессии по sessionId запрашиваются секции «section1» и «section2», которые будут загружены из ССД одновременно с поиском сессии. Именно с этими секциями планируется работа после получения сессии, а тот факт, что запрос нужных секций выполнен сразу при поиске сессии, позволяет избавиться от лишних REST-запросов к ССД (первое использование не запрошенной ранее секции загружает ее из ССД REST-запросом).

Использование характеристик поведения секций данных#

Каждая секция сессионных данных может иметь произвольный набор из следующих характеристик (enum SectionCharacteristic):

Характеристика

Активируемое поведение

USE_SUBSYSTEM_NAMESPACE

Секции с данной характеристикой хранятся в ССД в разрезе подсистем (subsystemCode). То есть в разных подсистемах имена таких секций могут совпадать, не приводя к коллизиям. Более того, имена таких секций могут совпадать с именами секций, общих для всех подсистем, не приводя к коллизиям. С другой стороны, никакая чужая подсистема не сможет получить доступ к такой секции, даже зная ее имя

SAVE_ONLY_IN_SIDE_CACHE

Секции с данной характеристикой хранятся только в slave-ССД и не реплицируются в master-ССД. Такие секции используются как сессионный Near Cache, самостоятельно восстанавливаемый в случае потери данных из master-систем потребителями ССД (т.е. сервис должен быть готов к возможной потере таких секций данных в ССД). При использовании данной характеристики учтите, что балансировщики работают в режиме StickySession, привязывая каждую подсистему (subsystemCode) в рамках одной сессии к одному конкретному серверу, на котором обязательно развернут slave-ССД. Таким образом, все запросы к прикладному сервису в рамках одной сессии будут иметь доступ к одному и тому же slave-ССД, а значит к одним и тем же SAVE_ONLY_IN_SIDE_CACHE-секциям

SAVE_ONCE

Секции с данной характеристикой можно сохранять только 1 раз, но читать можно многократно. Такие константные секции позволяют минимально обращаться к master-ССД, экономя тем самым сетевые ресурсы, если, конечно, используется кеширование на slave-ССД (отсутствует характеристика NON_SAVE_IN_SIDE_CACHE)

NON_SERIALIZABLE

Секции с данной характеристикой хранятся в ССД в открытом, а не в сериализованном виде. При этом типы значений в таких секциях ограничены обертками примитивов и строками. До появления характеристик этот аспект поведения был присущ только metaInfo-секции данных

NON_SAVE_IN_SIDE_CACHE

Секции с данной характеристикой хранятся только в master-ССД и не кешируются в slave-ССД. Такие секции никогда не требуют принудительного обновления (refresh) из master-ССД, но, естественно, за счет повышенной нагрузки на сеть

NON_COMPRESSIBLE

Секции с данной характеристикой хранятся в ССД не в сжатом (ZIP) виде. Данная характеристика применима только к секциям, НЕ имеющим характеристику NON_SERIALIZABLE

Следует отметить, что широко используемые до появления характеристик shared-секции не имеют ни одной характеристики, что является поведением секций данных по умолчанию. Характеристики можно «приклеить» к секции данных, только пока она новая (version = 0). После первого сохранения секция начинает свой жизненный цикл, используя поведение согласно ее характеристикам, поменять которые уже нельзя. Таким образом, с помощью характеристик можно создавать кастомные Local-, METAINFO- и многие другие секции. Из имеющихся 6-ти характеристик можно придумать 64 различных сочетания, т.е. различных по поведению типа секций.

Как работать с характеристиками секций данных в Java-коде, смотрите ниже.

public class SdsClientTest {
    // ...
    private SessionDataService sds;  // компонент Сессионные данные
    private String sessionId;

    /**
     * Работа с секцией данных, имеющей специфические характеристики поведения
     */
    @Test
    public void usageOfSectionWithCustomCharacteristics() {
        SdsSession session = sds.getSession( sessionId, sds.createSessionMetaInfo(),
                                             ImmutableMap.of() );

        gettingOfSectionWithCharacteristics( session, "section1",
                                             SectionCharacteristic.NON_COMPRESSIBLE );
        gettingOfSectionWithCharacteristics( session, "section2",
                                             SectionCharacteristic.USE_SUBSYSTEM_NAMESPACE,
                                             SectionCharacteristic.SAVE_ONLY_IN_SIDE_CACHE,
                                             SectionCharacteristic.SAVE_ONCE );
    }

    private void gettingOfSectionWithCharacteristics(
            SdsSession session, String sectionName, SectionCharacteristic... characteristics )
    {
        SdsSessionSection section1 = session.getSection( sectionName, characteristics );
        SdsSessionSection section2 = session.sections()
                                            .section( sectionName, characteristics )
                                            .getOne();

        assertTrue( section1.hasCharacteristics( characteristics ) );
        assertTrue( section2.hasCharacteristics( characteristics ) );
    }
    // ...

Работа с пространствами имен#

public class SdsClientTest {
    // ...
    private SessionDataService sds;  //  компонент Сессионные данные
    private String sessionId;
    /**
     * Работа с различными пространствами имен (namespaces)
    */
    @Test
    public void sectionsWithEqualNamesAndDifferentNamespacesMayBeUsed() {
        SdsSession session = sds.getSession( sessionId, sds.createSessionMetaInfo(),
                                             ImmutableMap.of() );

        SdsSessionSection section1 = session.getSection( "namespace1", "section" );
        SdsSessionSection section2 = session.getSection( "namespace2", "section" );
        SdsSessionSection section3 = session.getSection( "section" );
        section2.setAttribute( "attribute", 123 );
        sds.saveSessionSections( session, ImmutableMap.of() );

        SdsSessionSection section4 = session.getSection( "namespace1", "section" );
        SdsSessionSection section5 = session.getSection( "namespace2", "section" );
        SdsSessionSection section6 = session.getSection( "section" );

        assertNull( section4.getAttribute( "attribute" ) );
        assertEquals( section5.getAttribute( "attribute" ), Integer.valueOf( 123 ) );
        assertNull( section6.getAttribute( "attribute" ) );
    }

    /**
     * Работа с пространствами имен по умолчанию
     */
    @Test
    public void defaultSectionNamespaceMayNotBeSpecified() {
        SdsSession session = sds.getSession( sessionId, sds.createSessionMetaInfo(),
                                             ImmutableMap.of() );
        // По умолчанию в качестве namespace используется SdsSession.UNNAMED_NAMESPACE
        assertEquals( getSessionDefaultNamespace( session ), SdsSession.UNNAMED_NAMESPACE );

        // Для получения секций из namespace по умолчанию необязательно явно указывать его имя
        SdsSessionSection section1 = session.getSection( "section" );
        SdsSessionSection section2 = session.getSection( null, "section" );
        SdsSessionSection section3 = session.getSection( SdsSession.UNNAMED_NAMESPACE, "section" );
        SdsSessionSection section4 = session.sections()
                                            .section( "section" )
                                            .getOne();
        SdsSessionSection section5 = session.sections()
                                            .section( SdsSession.UNNAMED_NAMESPACE, "section" )
                                            .getOne();
        assertAllSame( section1, section2, section3, section4, section5 );
    }

    private String getSessionDefaultNamespace( SdsSession session ) {
        return session.getMetaInfo().getAttribute( SessionMetaAttribute.DEFAULT_NAMESPACE.key );
    }

    private void assertAllSame( Object... objects ) {
        for ( int i = 1; i < objects.length; ++i )
            assertSame( objects[0], objects[i] );
    }
    // ...

Инвалидация сессии ССД#

public class SdsClientTest {
    // ...
    private SessionDataService sds;  // компонент Сессионные данные
    private String sessionId;

    @AfterMethod
    public void invalidateSdsSession() {
        Map<String, Object> requestParameters =
                ImmutableMap.of( "invalidation.cause", "Client was logged out" );
        sds.invalidateSession( sessionId, requestParameters );
    }
    // ...

Следует отметить, что при удалении сессии можно указать причину удаления в виде строки. Данная причина будет отражена как в логах REST-запросов к ССД, так и в событии удаления сессии в компоненте Аудит (AUDT).

Примечание. В прикладных сервисах удаление сессии в ССД может понадобиться только в отдельных случаях, согласованных архитектурно.

Пример обработки исключений ССД#

Из любого метода класса SessionDataService клиентского модуля ССД (и из производных объектов) может быть вызвано исключение SdsClientException. Причем все исключения, явно генерируемые в ССД, имеют такой тип. Ниже смотрите пример, как следует обрабатывать (если это необходимо) исключения ССД:

public class SdsClientTest {
    // ...
    private SessionDataService sds;  // компонент Сессионные данные
    // ...
    /**
     * Обработка исключений ССД
     */
    @Test
    public void exceptionHandling() {
        try {
            // Запрашиваем несуществующую сессию, чтобы slave вызвал исключение
            sds.getSession( "nonExistentSessionId", sds.createSessionMetaInfo(),
                            ImmutableMap.of() );
        } catch ( SdsClientException e ) {
            // Код ошибки, возникшей непосредственно в клиентском модуле ССД
            assertEquals( e.getCode(), "UFS_SESSION:REQUEST_FORBIDDEN" );
            // Код ошибки, возникшей в back ССД и приведшей к ошибке клиентского модуля
            assertTrue( e.getBackCode() == null ||
                        e.getBackCode().equals( "UFS_SESSION:LOCATION_EXTRACTION_ERROR" ) );
        }
    }
    // ...

Разграничение прав доступа к секциям ССД#

ССД предоставляет возможность определить права доступа к секциям сессии.

Права доступа задаются для сессии и состоят из правил. Каждое правило определяет разрешение или запрет на чтение/запись секций для указанных подсистем. Правило представляет собой пару вида: subsystemCodeMask, sectionNamespaceMask[.sectionNameMask]; r|w|rw[; allow|forbid], где:

  • subsystemCodeMask – это маска подсистем, для которых определено правило;

  • sectionNamespaceMask – это маска пространств имен секций (namespaces);

  • sectionNameMask – это маска названий секций, которая не является обязательной;

  • r|w|rw – это обозначения действий, которые необходимо разрешить или запретить соответственно: чтение, запись, чтение и запись;

  • allow|forbid – это разрешение или запрет на чтение и/или запись секций.

Права доступа задаются для сессии либо при ее создании, либо методом setAccessRights().

Права доступа по умолчанию определяются meta-атрибутом DEFAULT_SECTIONS_ACCESSIBILITY при создании сессии. Если его не указать, то значение по умолчанию false (секции не доступны, если для них явно не задано allow). Если meta-атрибут DEFAULT_SECTIONS_ACCESSIBILITY задан при создании сессии, то его значение следует уточнять у сервиса, создающего сессии.

При чтении и сохранении секций происходит проверка прав доступа для той подсистемы, которая пытается это сделать. Если для одной и той же секции данных найдены правила и с позволением (allow), и с запретом (forbid), то применяется запрет.

public class SdsClientTest {
    // ...
    private SessionDataService sds;  // ССД
    private String sessionId;

    /**
     * Разграничение прав доступа к секциям ССД
     */
    @Test
    public void accessRightsUsage() {
        SdsSession session = sds.getSession( sessionId, getMetaInfoWithClientIdAndIp(), ImmutableMap.of() );
        List<Map.Entry<String, String>> accessRights =
                ImmutableList.of( Pair.of( "*", "*; rw; allow" ),
                                  Pair.of( "S1", "ns.sectionName; w; forbid" ),
                                  Pair.of( "S2", "ns.otherSectionName; rw; forbid" ),
                                  Pair.of( "S2", "ns.otherSectionName; r; allow" ) );
        session.setAccessRights( accessRights );
        sds.saveSessionSections( session, ImmutableMap.of() );

        // Согласно второму правилу подсистеме S1 запрещено записывать секцию ns.sectionName, но не запрещено ее читать
        mockSubsystem( "S1" );
        assertReadAccessAllowed( session, "ns", "sectionName" );
        assertWriteAccessForbidden( session, "ns", "sectionName" );

        // По третьему правилу подсистеме S2 запрещено читать и записывать секцию ns.otherSectionName,
        // но по четвертому правилу чтение разрешено. Запрет на чтение/запись имеет больший приоритет,
        // чем разрешение, поэтому подсистеме S2 запрещено читать секцию ns.otherSectionName.
        mockSubsystem( "S2" );
        assertReadAccessForbidden( session, "ns", "otherSectionName" );
        unmockSubsystem();
    }

    private void assertReadAccessAllowed( SdsSession session, String namespace, String name ) {
        session.getSection( namespace, name );
    }

    private void assertWriteAccessForbidden( SdsSession session, String namespace, String name ) {
        SdsSessionSection section = session.getSection( namespace, name );
        section.setAttribute( "someAttribute", "someValue" );
        SdsClientException e = expectThrows( SdsClientException.class,
                                             () -> sds.saveSessionSections( session, ImmutableMap.of() ) );
        assertAccessForbidden( e );
        section.removeAttribute( "someAttribute" );
    }

    private void assertAccessForbidden( SdsClientException e ) {
        assertEquals( e.getCode(), PlatformErrorCodes.SESSION_EXCEPTION_200.getCode() );
        assertTrue( e.getBackCode() == null || // В LOCAL-режиме нет никакого кода ошибки, возникшего в back-е ССД
                    e.getBackCode().equals( "UFS_SESSION:ACCESS_TO_SECTION_DENIED" ) );
    }

    private void assertReadAccessForbidden( SdsSession session, String namespace, String name ) {
        SdsClientException e = expectThrows( SdsClientException.class,
                                             () -> session.getSection( namespace, name ) );
        assertAccessForbidden( e );
    }
    // ...

Все маски (имен подсистем, имен секций и их пространств имен) могут быть составными, разделенными символом «|». Также все маски могут быть инвертированными посредством добавления знака «!» в начале маски. Знаки «|» и «!» не относятся к части соответствующего имени, таким образом, налагается ограничение на имена: они не могут начинаться со знака «!» и содержать знак «|».

// ...
/**
 * Использование составных и инвертированных масок для разграничения прав доступа к секциям ССД
 */
@Test
public void accessRightsUsageWithComplexMasks() {
    SdsSession session = sds.getSession( sessionId, getMetaInfoWithClientIdAndIp(), ImmutableMap.of() );
    List<Map.Entry<String, String>> accessRights =
            ImmutableList.of( Pair.of( "*", "*; rw; allow" ),
                              Pair.of( "S1|S2", "!ns.name*; r; forbid" ) );
    session.setAccessRights( accessRights );
    sds.saveSessionSections( session, ImmutableMap.of() );

    // Согласно второму правилу подсистемам S1 и S2 запрещено читать секции, у которых
    // namespace отличается от "ns", а имя начинается с "name"
    mockSubsystem( "S1" );
    assertReadAccessAllowed( session, "ns", "name" );
    assertReadAccessForbidden( session, "otherNs", "name" );
    assertReadAccessForbidden( session, "otherNs", "nameWithSuffix" );
    mockSubsystem( "S2" );
    assertReadAccessAllowed( session, "otherNs", "otherName"  );
    assertReadAccessForbidden( session, "otherNs", "nameWithSuffix" );

    // А для подсистемы S3 разрешен любой доступ, т.к. она подходит только под первое правило
    mockSubsystem( "S3" );
    assertReadAccessAllowed( session, "otherNs", "nameWithSuffix"  );
    assertWriteAccessAllowed( session, "otherNs", "nameWithSuffix"  );
    unmockSubsystem();
}

private void assertWriteAccessAllowed( SdsSession session, String namespace, String name ) {
    SdsSessionSection section = session.getSection( namespace, name );
    section.setAttribute( "someAttribute", "someValue" );
    sds.saveSessionSections( session, ImmutableMap.of() );
}
// ...

Использование sidecar slave-ССД#

С самого начала одной из ключевых особенностей ССД была возможность хранить данные на localhost, не передавая их по сети вообще. Для этого изначально использовались local-секции, а позже SAVE_ONLY_IN_SIDE_CACHE-секции. Доступ к таким секциям данных очень быстрый за счет использования липкой сессии, поскольку данные не нужно искать по сети, они уже расположены на узле прикладного сервиса, куда пришел запрос, требующий сессионных данных. Однако при разворачивании сервиса в облачной инфраструктуре приходится корректировать работу с private-данными. Это связано с механизмами автомасштабирования (autoscaling), проверки работоспособности (healthcheck) и прочих, при которых оркестратор может динамически останавливать/запускать контейнеры с развернутыми в них сервисами. Поскольку slave-ССД разворачивается в виде sidecar в одном pod с сервисом-потребителем, то при штатной остановке сервиса-потребителя будут безвозвратно потеряны данные, хранимые только на остановленном slave-ССД (private-данные). Другой slave-ССД, запущенный вместо остановленного, не будет иметь тех данных, что были потеряны. Для достижения cloud-ready состояния каждому сервису-потребителю ССД требуется выполнить описанные ниже действия.

Репликация критичных local-данных в master-ССД#

Local-секции хранятся только в slave-ССД, а значит в k8s эти данные могут быть потеряны в произвольный момент времени, что приведет к прерыванию процесса. Чтобы процесс не прерывался, разработчикам сервиса-потребителя ССД требуется разделить данные, сохраняемые в Local-секцию, на НЕкритичные (можно восстановить при потере) и критичные (восстановить невозможно, отсутствие прерывает бизнес-процесс).

Работу с НЕкритичными данными нужно построить таким образом, чтобы «быть готовыми» к их потере и восстановлению из master-систем. Например, так:

 public Data getNonCriticalData() {
     Data data = sdsSession.getPrivateSection().getAttribute("dataKey");
     if (data == null) {
         data = someMasterService.runHeavyDataReceiving();
         sdsSession.getPrivateSection().setAttribute("dataKey", data);
     }
     return data;
 }

То есть НЕкритичные данные можно продолжить хранить в Local-секции, чтобы не нагружать master-ССД. Работу с критичными данными нужно перевести на собственные Local-секции, сохраняемые в master-ССД. Например, так:

 public Data getCriticalData() {
     SdsSessionSection sdsSection = sdsSession.sections()
                                              .section("ownPublicSection", SectionCharacteristic.USE_SUBSYSTEM_NAMESPACE)
                                              .getOne();
     Data data = sdsSection.getAttribute("dataKey");
     // Данные сохранены где-то в другом месте и не могут быть потеряны, т.к. реплицированы в master-ССД
     return Validate.notNull(data);
 }

То есть критичные данные нужно перестать хранить в Local-секции, чтобы избежать их потери. Разделить данные на некритичные и критичные может только сам сервис-потребитель ССД, т.к. эти данные относятся к его предметной области. После такой доработки pod может быть безопасно остановлен, т.к. его работу сможет продолжить любой другой pod с «чистым» slave-ССД (НЕкритичные данные он восстановит, а критичные получит с master-ССД).

Репликация данных Workflow в master-ССД#

Чтобы фронтальный сценарий не прерывался, разработчикам сервиса-потребителя ССД и Workflow требуется указать модулю Workflow реплицировать все данные в master-ССД.

  1. Сервису, разворачиваемому в k8s, следует нести в своей поставке настройку: ufs.wf.use.data.replication = true (разрез Технологического ядра: «SUBSYSTEM», «CHANNEL»). По умолчанию данный флаг равен false. Взведение данного флага заставляет модуль Workflow сохранять все свои данные не только в slave-ССД, но и в master-ССД.

  2. Если требуется более «тонкий» перевод сервиса в облако (например, отдельными модулями - EAR), который невозможно реализовать через настройку, то можно переопределить WorkflowConfiguration:

<workflow:configuration id="workflowConfiguration">
    <workflow:custom id="customConfiguration"/>
</workflow:configuration>
<bean id="customConfiguration" class="{домен}.ufs.some.service.WorkflowCustomConfiguration"/>
public class WorkflowCustomConfiguration implements WorkflowConfiguration {
    ...
    @Override public ConfigProvider getConfigProvider() {
        return new CustomConfigProvider();
    }
    ...
}
public class CustomConfigProvider implements ConfigProvider {
    ...
    @Override public boolean useDataReplication() {
        return /* вычислить флаг по-своему */;
    }
}

После таких изменений работы с модулем Workflow, pod может быть безопасно остановлен, т.к. его работу сможет продолжить любой другой pod с «чистым» slave-ССД, который получит все данные Workflow с master-ССД.

Примечание. Сервисам, включившим репликацию данных Workflow в master-ССД, следует оценить объем данных, которые сохраняются во flowScope Workflow. Нужно понимать, что все эти данные теперь создадут дополнительный сетевой трафик с master-ССД. Серьезное увеличение данного трафика может негативно сказаться на НТ. В случае сложных задач (например, сохранение PDF во flowScope Workflow) можно воспользоваться гибридным вариантом доступа к данным ССД: не класть тяжелые данные во flowScope, а прямо в EventHandler Workflow сохранять их в Local-секцию, используя API ССД. Естественно, что нужно быть готовыми восстановить эти тяжелые данные в случае потери.

Использование Session management API#

Session management API реализует приложение servant-ССД.

Все запросы к servant-ССД используют endpoint: {props.ufs.baseurl.sds.servant}/ufs-session-servant/rest/v1/management/.

Для того чтобы использовать Session management API, необходимо получить baseUrl servant-ССД через глобальные параметры следующим образом:

# Базовый Url доступа к servant-ССД
props.ufs.baseurl.sds.servant=!${global.platform.ufs.baseurl.sds.servant}
global.platform.ufs.baseurl.sds.servant=${global.platform.sgw.protocol.session.default}://${global.platform.sgw.route.session.host}:${global.platform.sgw.port.session.default}

# global.platform.sgw.route.session (балансировщик в cекторе активных сессий)

Получение количества сессий ССД (/getSessionsCount)#

REST-сервис /getSessionsCount в Session management API.

Запрос для REST-сервиса имеет следующие поля:

public class SessionsCountRq {
    public final String clientId;
    public final String authType;
    public final String channel;

Кроме того, в заголовке UFS-TENANT REST-запроса передается тенант, которому должны принадлежать запрашиваемые сессии ССД.

Ответ REST-сервиса имеет следующие поля:

public class SessionsCountRs {
    public final Long sessionsCount;

То есть возвращается количество сессий, принадлежащих заданному тенанту и совпадающих по clientId, authType и channel.

Инвалидация сессий ССД, привязанных к внешним сессиям (/invalidateSessions)#

REST-сервис /invalidateSessions в Session management API.

Запрос для REST-сервиса имеет следующие поля:

public class SessionsInvalidateRq {
    public final String sessionOwnerType;
    public final Map<String, List<String>> sessionsLinkedToUfs;

В Map sessionsLinkedToUfs ключом является причина инвалидации сессий, а значением – список идентификаторов, привязанных к сессиям ССД внешних сессий, которые нужно инвалидировать.

Кроме того, в заголовке UFS-TENANT REST-запроса передается тенант, которому должны принадлежать запрашиваемые сессии ССД.

Ответ REST-сервиса имеет следующие поля:

public class SessionsInvalidateRs {
    public final Map<String, List<SessionInvalidateRs>> sessionsLinkedToUfs;

public class SessionInvalidateRs {
    public final String linkedToUfsSessionId;
    public final String invalidationError;

В Map sessionsLinkedToUfs ключом является причина инвалидации сессий, а значением – список идентификаторов внешних сессий, привязанных к сессиям ССД, с возможными ошибками инвалидации по каждой сессии. Поле invalidationError имеет значение «null», если сессия успешно инвалидирована или не найдена. В ответе содержатся только те инвалидированные внешние сессии, которые были привязаны к сессиям ССД, принадлежащим заданному тенанту и относящимся к указанному типу sessionOwnerType.

Поиск сессий ССД, привязанных к внешним сессиям (/findUfsSessionsByExternal)#

REST-сервис /findUfsSessionsByExternal в Session management API.

Запрос для REST-сервиса имеет следующие поля:

public class FindUfsSessionsRq {
    public final String sessionOwnerType;
    public final String linkedToUfsSessionId;

Кроме того, в заголовке UFS-TENANT REST-запроса передается тенант, которому должны принадлежать запрашиваемые сессии ССД.

Ответ REST-сервиса имеет следующие поля:

public class FindUfsSessionsRs {
    public final List<FindUfsSessionRs> ufsSessions;

public class FindUfsSessionRs {
    public final String ufsSessionId;

То есть возвращается список сессий ССД, принадлежащих заданному тенанту, относящихся к указанному типу sessionOwnerType и привязанных к сессии ССД с идентификатором linkedToUfsSessionId.

Технически, таких сессий может быть несколько, это зависит от компонента, создающего сессию (например, Стартового менеджера (SGWX)). По контракту ССД со Стартовым менеджером к одной внешней сессии должна быть привязана только одна сессия ССД (однако к одной сессии ССД может быть привязано несколько внешних).

Уведомления от servant-ССД#

Запрос уведомлений имеет следующие поля:

public class SessionNotificationsRq implements Serializable {
    public final List<SessionNotificationRq> sessions;

public class SessionNotificationRq implements Serializable {
    /** Идентификатор внешней сессии, связанной с сессией ЕФС
     * @deprecated начиная с R19.5.2 используются поля {@link #eventParms} и {@link #ufsSessionId} */
    public final String linkedToUfsSessionId;
    /** @since R19.5.2 */
    public final String ufsSessionId;
    /** Событие, произошедшее с сессией */
    public final SessionEventType eventType;
    /** Время возникновения события (может быть {@code null})
     * @since R19.5.2 */
    public final Calendar eventTime;
    /** Причина возникновения события (может быть {@code null})
     * @since R19.5.2 */
    public final String eventCause;
    /** @since R19.5.2 */
    public final Map<String, Serializable> eventParms;
    /** @deprecated Начиная с 19.5.2 следует использовать поле {@link #eventTime}*/
    public final Calendar lastAccessTime;

public enum SessionEventType {
    INVALIDATION
}

Часто встречающиеся проблемы и пути их устранения#

1. Ошибка при создании сессии, запрос newSession не отрабатывает.

  • Что происходит

В логах slave-ССД ошибка создания сессии: не все обязательные атрибуты [channel, username, clientIp] присутствуют среди атрибутов

[
defaultNamespace: ${UNNAMED},
channel: WEB,
username: someuser
]

В запросе не хватает clientIp.

  • Решение

Убедиться, что к сервису-потребителю приходит HTTP-заголовок X-Forwarded-For. Проверить значение параметра platform.client.ip.header. Оно должно быть установлено в X-FORWARDED-FOR для корректной работы.

2. Потребители ССД при попытке обращения к данным несуществующей сессии могут столкнуться со следующими проблемами:

  1. Ошибка с кодом 403 («Попытка обратиться к данным несуществующей сессии») в ответ на HTTP-запрос.

  2. Исключение SdsClientException с кодом UFS-SESSION:REQUEST_FORBIDDEN, получаемое от КМ ССД в коде сервиса-потребителя.

  • Что происходит

Проверка существования сессии в КМ теперь осуществляется методом boolean isSessionExists (String sessionId). Данный метод возвращает true, если сессия существует. Это значит, что при запросе сессии она вернется в ответе, и не будет 403 кода. Возвращает false, если сессия не существует (инвалидирована, никогда не существовала) и при запросе сессии вернется 403 код.

  • Решение

Для решения проблемы необходимо пересоздать сессию. Потребитель должен пройти повторную аутентификацию и авторизацию.

3. Как при разработке сервиса на машине разработчика использовать ССД, развернутый на стенде?

  • Что происходит

Приложение UFS_APP1 не видит сохраненные в сессию данные приложения UFS_APP2. Даже несмотря на подмену настройки в parameters.json (в developer mode), запросы от приложений уходят в локальный slave-ССД (localhost), который на машине разработчика не запущен.

  • Решение

В файле с настройками приложения (parameters.json), который используется в developer mode, достаточно скорректировать значение настройки ufs.sds.rest.url, указав там URL развернутого на стенде slave-ССД.

4. В сессии отсутствует ожидаемый результат. Клиентский код читает значение по ключу и вместо ожидаемого значения получает null.

  • Решение

  1. Найти ID сессии, из которой читались данные. В АРМ Журналирования в записи об ошибке должно присутствовать поле «идентификатор сессии».

  2. Проверить, точно ли значение в сессию кто-то записал ранее?

    1. Найти в Журналировании все события по данному ID сессии.

    2. Убедиться, что ошибке предшествовала запись о событии, при котором данные были сохранены в сессию. При этом запрос, при обработке которого данные были сохранены в сессию, должен был закончиться успешно.

  3. Убедиться, что данные читаются из той же секции и по тому же ключу, как при сохранении.

  4. Если данные сохранялись в local-секцию (методом sdsSession.getPrivateSection().setAttribute(), либо через обертку Технологического ядра платформы - {домен}.ufs.platform.core.env.SessionContextImpl#setAttribute()), то необходимо убедиться, что корректно работает «липкая сессия» (sticky session) на балансировщике перед сервисом-потребителем. Проверить, что оба запроса – сохранивший данные в сессию и читающий их оттуда – выполняются на одном и том же физическом узле.

Приложения#

Приложение 1. Содержимое файла sds-slave-config.yaml.

apiVersion: v1
kind: ConfigMap
metadata:
  name: <your-fpi-name>.sds-slave-config.{{ lookup('custom_vars', 'distrib.release.version') }}
data:
  ufs-session-slave.__{{ lookup('custom_vars', 'ufs_session_slave_sup2_scopeTemplate') }}.json: |
    {
      "version": "1.0",
      "parameters": [
        {
          "name": "healthcheck.metric.task.attempt.period",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "DEPLOYMENT_UNIT",
                  "value": null
                },
                {
                  "code": "CHANNEL",
                  "value": null
                }
              ],
              "values": [
                "2000"
              ]
            }
          ]
        },
        {
          "name": "healthcheck.cascadeprotection.attempt.period",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "DEPLOYMENT_UNIT",
                  "value": null
                },
                {
                  "code": "CHANNEL",
                  "value": null
                }
              ],
              "values": [
                "10000"
              ]
            }
          ]
        },
        {
          "name": "healthcheck.cascadeprotection.chatter.step.count",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "DEPLOYMENT_UNIT",
                  "value": null
                },
                {
                  "code": "CHANNEL",
                  "value": null
                }
              ],
              "values": [
                "5"
              ]
            }
          ]
        },
        {
          "name": "healthcheck.cascadeprotection.chatter.percent",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "DEPLOYMENT_UNIT",
                  "value": null
                },
                {
                  "code": "CHANNEL",
                  "value": null
                }
              ],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "healthcheck.metric.logger.attempt.period",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "DEPLOYMENT_UNIT",
                  "value": null
                },
                {
                  "code": "CHANNEL",
                  "value": null
                }
              ],
              "values": [
                "600000"
              ]
            }
          ]
        },
        {
          "name": "healthcheck.metric.logger.level",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "DEPLOYMENT_UNIT",
                  "value": null
                },
                {
                  "code": "CHANNEL",
                  "value": null
                }
              ],
              "values": [
                "WARN"
              ]
            }
          ]
        },
        {
          "name": "healthcheck.cascadeprotection.errorcode",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "DEPLOYMENT_UNIT",
                  "value": null
                },
                {
                  "code": "CHANNEL",
                  "value": null
                }
              ],
              "values": [
                "{{ lookup('custom_vars', 'healthcheck.cascadeprotection.serviceunavailable') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.baseUrl.openshift",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_baseUrl_insecure') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.baseUrl",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_baseUrl_secure') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.delay",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_delay') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.max.file.size",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_max_file_size') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.min.file.size",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_min_file_size') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.ignore.file.pattern",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_ignore_file_pattern') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.httpclient.circuitBreaker.enable",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_httpclient_circuitBreaker_enable') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.httpclient.circuitBreaker.failureRate.threshold",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_httpclient_circuitBreaker_failureRate_threshold') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.httpclient.circuitBreaker.openState.duration",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_httpclient_circuitBreaker_openState_duration') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.httpclient.circuitBreaker.halfOpen.requestBuffer",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_httpclient_circuitBreaker_halfOpen_requestBuffer') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.httpclient.circuitBreaker.minimumCalls.size",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_httpclient_circuitBreaker_minimumCalls_size') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.agent.startup.synchronization.timeout",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_agent_startup_synchronization_timeout') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.tenant.code",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_module_tenant_code') }}"
              ]
            }
          ]
        },
        {
          "name": "ufsparams.module.monitoring.enabled",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'ufsparams_module_monitoring_enabled') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.logger.debug.enabled",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.monitoring.async.send.metrics",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "true"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.monitoring.async.queue.max.size",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "100000"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.monitoring.metrics.sending.periodicity",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "10000"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.master.context.path",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.sds.master.balancer.deploy.unit.name') }}/rest"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.direct.master.context.path",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.sds.direct.master.balancer.deploy.unit.name') }}/rest"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.target.node.header.name",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "UFS-GO-NODE"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.target.node.header.tmp.forced.value",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "-"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.target.node.random.selection.header.name",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "UFS-REPLICA-KEY"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.pulling.periodicity",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "30000"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.pulling.max.session.count",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "500"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.thread.lock.fairness",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.thread.lock.max.wait.timeout",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "500"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.errors.count.for.master.deactivation",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "3"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.master.success.response.trust.duration.for.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "3000"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.slave.min.recommended.version",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "D-05.000.00"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.slave.min.accepted.version",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "D-05.000.00"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.improved.session.data.format",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.sds.one.nio.session.data.format') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.fine.grained.session.data.format",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.sds.one.nio.session.data.format') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.slave.cache.ttl.percentage",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "35"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.cm.one.nio.serializers.auto.exchange",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.slave.one.nio.serializers.auto.exchange",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.validate.proxy.sections.by.master.by.default",
          "isList": true,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "DEFAULT=true"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.stale.cache.detection.request.headers",
          "isList": true,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "-"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.stale.cache.detection.response.header",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "-"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.subsystem.slave.change.action.duration",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "1000"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.used.slave.rest.api.version.map",
          "isList": true,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": "SESSION_REGISTRY"
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "DEFAULT=3"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.local.sections.forbidden.by.default",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.sds.local.sections.forbidden.by.default') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.master.sync.base.url",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.sds.master.sync.base.url') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.master.sync.trust.duration.for.behaviour.params",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "SDS_VERSION",
                  "value": null
                }
              ],
              "values": [
                "3000"
              ]
            }
          ]
        },
        {
          "name": "ufs.sds.rest.url",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                },
                {
                  "code": "CHANNEL",
                  "value": null
                }
              ],
              "values": [
                "http://localhost:9080/ufs-session-slave/rest"
              ]
            }
          ]
        },
        {
          "name": "ufs.baseurl.sds.master",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                }
              ],
              "values": [
                "http://unused.baseurl.host:80"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.connections.max.total.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "150"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.connections.max.per-route.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "150"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.time-out.request.milliseconds.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "3000"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.time-out.connection.milliseconds.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "500"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.enable.sds.master",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.failure-rate.threshold.percent.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.closed.request-buffer.size.request-count.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "120"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.half-open.request-buffer.size.request-count.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "0"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.open.duration.milliseconds.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "60000"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.record.exceptions.sds.master",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": []
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.record.httpException.codes.sds.master",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": []
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.upper.requests-per-second.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "2"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.start.percent.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.change-step.percent.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "5"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.enable.sds.master",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "true"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.max-attempts.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "30"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.max-concurrent-calls.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "150"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.wait-duration.sds.master",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "100"
              ]
            }
          ]
        },
        {
          "name": "ufs.baseurl.sds.master.pulling",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                }
              ],
              "values": [
                "http://unused.baseurl.host:80"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.connections.max.total.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "10"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.connections.max.per-route.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "10"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.time-out.request.milliseconds.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "3000"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.time-out.connection.milliseconds.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "500"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.enable.sds.master.pulling",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.failure-rate.threshold.percent.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.closed.request-buffer.size.request-count.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "120"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.half-open.request-buffer.size.request-count.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "0"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.open.duration.milliseconds.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "60000"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.record.exceptions.sds.master.pulling",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": []
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.record.httpException.codes.sds.master.pulling",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": []
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.upper.requests-per-second.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "2"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.start.percent.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.change-step.percent.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "5"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.enable.sds.master.pulling",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.max-attempts.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "30"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.max-concurrent-calls.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "8"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.wait-duration.sds.master.pulling",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "100"
              ]
            }
          ]
        },
        {
          "name": "ufs.baseurl.sds.master.direct",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                }
              ],
              "values": [
                "http://unused.baseurl.host:80"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.connections.max.total.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "10"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.connections.max.per-route.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "10"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.time-out.request.milliseconds.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "3000"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.time-out.connection.milliseconds.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "500"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.enable.sds.master.direct",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.failure-rate.threshold.percent.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.closed.request-buffer.size.request-count.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "120"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.half-open.request-buffer.size.request-count.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "0"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.open.duration.milliseconds.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "60000"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.record.exceptions.sds.master.direct",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": []
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.record.httpException.codes.sds.master.direct",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": []
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.upper.requests-per-second.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "2"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.start.percent.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.change-step.percent.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "5"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.enable.sds.master.direct",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "true"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.max-attempts.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "30"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.max-concurrent-calls.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "150"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.wait-duration.sds.master.direct",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "100"
              ]
            }
          ]
        },
        {
          "name": "ufs.baseurl.sds.master.healthcheck",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [
                {
                  "code": "SUBSYSTEM",
                  "value": null
                }
              ],
              "values": [
                "http://unused.baseurl.host:80"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.connections.max.total.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.httpclient.connections.max.total.sds.master.healthcheck') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.connections.max.per-route.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.httpclient.connections.max.per-route.sds.master.healthcheck') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.time-out.request.milliseconds.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "{{ lookup('custom_vars', 'props.ufs.httpclient.time-out.request.milliseconds.sds.master.healthcheck') }}"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.time-out.connection.milliseconds.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "500"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.enable.sds.master.healthcheck",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "false"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.failure-rate.threshold.percent.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.closed.request-buffer.size.request-count.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "120"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.half-open.request-buffer.size.request-count.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "0"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.state.open.duration.milliseconds.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "60000"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.record.exceptions.sds.master.healthcheck",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": []
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.record.httpException.codes.sds.master.healthcheck",
          "isList": false,
          "type": "STRING",
          "bundle": [
            {
              "path": [],
              "values": []
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.upper.requests-per-second.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "2"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.start.percent.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "50"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.circuit-breaker.smooth-start.rate-limit.change-step.percent.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "5"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.enable.sds.master.healthcheck",
          "isList": false,
          "type": "BOOLEAN",
          "bundle": [
            {
              "path": [],
              "values": [
                "true"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.max-attempts.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "30"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.max-concurrent-calls.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "150"
              ]
            }
          ]
        },
        {
          "name": "ufs.httpclient.bulkhead.wait-duration.sds.master.healthcheck",
          "isList": false,
          "type": "LONG",
          "bundle": [
            {
              "path": [],
              "values": [
                "100"
              ]
            }
          ]
        }
      ]
    }