Групповые действия
Примечание
Доступно только в Enterprise версии.
Групповые действия позволяют обработать большое количество элементов на сервере.
Примеры групповых действий:
Выгрузка в Excel
Скачивание Zip архива со всеми выбранными элементами
Лицензия
Функционал групповых действий доступен если в разделе features содержимого лицензии есть запись group-actions:
features:
group-actions: {}
Описание новых групповых действий в микросервисе
Для добавления возможности описывать групповые действия в микросервисе нужно добавить в pom.xml зависимость:
<!-- Подходящая версия подтянется из родительского pom файла -->
<dependency>
<groupId>ru.citeck.ecos.ent.groupactions</groupId>
<artifactId>ecos-group-actions</artifactId>
</dependency>
После этого можно описывать свою реализацию следующего интерфейса как spring-компонент (он зарегистрируется автоматически):
interface GroupActionExecutionFactory<in T : Any, in C : Any> {
fun createExecution(config: C): GroupActionExecution<T>
fun getType(): String
}
Метод getType должен вернуть тип, по которому можно будет вызвать реализуемое действие.
Метод createExecution вызывается каждый раз когда запускается новое действие.
Интерфейс GroupActionExecution выглядит следующим образом:
interface GroupActionExecution<in T : Any> {
fun getRequiredAttributes(): Map<String, *> {
return emptyMap<String, String>()
}
@Throws(Exception::class)
fun execute(context: GroupActionContext<T>): ActionResult
@Throws(Exception::class)
fun dispose()
}
Метод getRequiredAttributes возвращает мапу с атрибутами, которые нужны для выполнения действия. Атрибуты можно запрашивать и в ходе выполнения действия, но эффективнее вернуть их список заранее, чтобы не делать лишних запросов.
Метод dispose вызывается когда действие завершилось с любым результатом. Т.е. в случае ошибки метод dispose будет так же вызван.
Метод execute вызывается в момент запуска действия. Все основные манипуляции с данными выполняется именно в этом методе.
В метод execute передается контекст действия с методами getValues(): Iterable<T> и getBatchedValues(batchSize: Int): Iterable<List<T>>. Можно вызвать любой из этих методов и перебрать все значения через цикл или вручную.
Если предполагается, что при возникновении ошибки обработку не нужно прерывать, то исполнитель действия должен сам это учитывать. В случае выбрасывания исключения из метода execute выполнение действия сразу завершается.
Варианты результата групповой операции:
Класс |
ID |
Данные |
Описание |
---|---|---|---|
ActionResultLink |
LINK |
url: String |
Вернуть ссылку на что-то для скачивания.
Ссылка должна быть относительной (без протокола, хоста, порта).
Например:
Для получения ссылки на скачивание контента записи можно пользоваться методом getDownloadUrl в сервисе EcosContentApi.
Там же есть API для создания временного файла.
|
ActionResultMessage |
MESSAGE |
message: String |
Вернуть сообщение |
ActionResultOk |
OK |
Вернуть простой результат о том что действие успешно выполнено |
|
ActionResultResults |
RESULTS |
results: List<Result>
Типы:
Result:
message: String,
status: ResultStatus,
recordRef: EntityRef
enum ResultStatus {
OK,
ERROR,
PERMISSION_DENIED,
SKIPPED
}
|
Вернуть список результатов по каждой обрабатываемой записи |
Потоки исполнения
Все групповые действия выполняются в рамках преднастроенного тред пула.
Настройка количества потоков исполнения через spring свойства:
ecos:
webapp:
task:
executors:
group-actions:
corePoolSize: 5 # по умолчанию действия выполнают пять потоков
Выполнение действия происходит в контексте пользователя, который его инициировал (права доступа, часовой пояс, локаль и т.д.)
Запуск группового действия
Для запуска группового действия необходимо подготовить следующие параметры:
GroupActionParams(
values: GroupActionValuesParams, # параметры формирования списка значений для обработки
execution: GroupActionExecutionParams # параметры обработки
)
GroupActionValuesParams(
type: String, # тип источника значений для обработки
config: ObjectData, # конфигурация для формирования списка значений для обработки
limit: Long = -1 # ограничение на количество элементов в списке значений. -1 - без ограничений
)
GroupActionExecutionParams(
type: String, # тип действия
config: ObjectData = ObjectData.create(), # конфигурация действия
timeout: Duration = Duration.ofHours(5) # ограничение на время выполнения
)
Источники значений:
Тип |
Конфигурация |
Описание |
---|---|---|
records-list |
records: List<EntityRef>
|
Список конкретных сущностей |
records-query |
query: RecordsQuery
pageSize: Int
|
Список конкретных сущностей |
Архитектура решения
Общий принцип работы групповых действий следующий:
При добавлении зависимости ecos-group-actions Spring Boot автоконфигурация регистрирует:
Сервис групповых действий
Регистратор групповых действий
RecordsDAO для групповых действий с ID «group-action»
Для запуска группового действия выполняется мутация записи:
{appName_микросервиса}/group-action@
В атрибутах заполняются поля для GroupActionParams (т.е. values и execution)
RecordsDao через сервис запускает групповую операцию и сразу же (не дожидаясь её завершения) возвращает ссылку на созданное действие в следующем виде:
{appName_микросервиса}:{appInstance_микросервиса}/group-action@{actionId}
Например:
transformations:nrfdsvbocapo/group-action@7c269f9c-262b-4426-8865-7309dec07f2c
Далее инициатор действия может загрузить по вернувшемуся рефу атрибуты для получения информации о состоянии действия:
status: GroupActionStatus
initiator: EntityRef
processedCount: Long
totalCount: Long
result: GroupActionResult? # результат выполнения. В статусах WAITING и RUNNING всегда возвращает null. В остальных случаях всегда возвращается не-null значение.
Типы:
enum GroupActionStatus = {
WAITING, // действие ожидает пока освободится поток для его выполнения
RUNNING, // действие выполняется
COMPLETED, // действие завершено успешно
ERROR // ошибка при выполнении действия
}
GroupActionResult(
type: String, # Тип результата. Может быть одним из штатных типов результата (LINK, OK, и т.д.) или ошибочным - "ERROR"
data: ObjectData # Данные по результату. Например, для LINK здесь будет ссылка, для OK пустой объект.
)
Инициатор действия периодически может проверять состояние действия через загрузку нужных атрибутов и может выполнить какие-либо действия как только получит статус отличный от WAITING/RUNNING или ненулевой результат.
UI действие
Тип действия - server-group-action-v2
Конфиг действия:
targetApp: String # целевое приложение где описана реализация групповой операции
valuesParams:
limit: Number # Лимит обрабатываемых элементов
executionParams:
type: String # Тип действия
timeout: Duration # Максимальное время, которое действие может выполняться
config: Map<String, *> # Конфигурация действия. Содержимое зависит от типа действия
outputParams:
type: String # Тип способа возвращения результата действия
config: Map<String, *> # Конфигурация, описывающая дополнительные свойства для возвращения результата действия
outputParams не является обязательным параметром и прописывается в том случае, если необходимо исправить дефолтный способ ответа действия.
Пример конфигурации:
id: group-action-export-csv
type: server-group-action-v2
name:
ru: Скачать CSV-файл
en: Download Excel-file
config:
targetApp: transformations
valuesParams:
limit: 1000000
executionParams:
type: export-xlsx
timeout: T1H
config:
fileName: "report"
columns: [{name: Column, attribute: "?disp"}]
features:
execForRecords: true
execForQuery: true
execForRecord: false
При выполнении групповой операции отображается окно с прогрессом (% выполнения):
Пример конфигурации с outputParams:
id: group-action-export-xlsx
type: server-group-action-v2
name:
ru: Скачать Excel-файл
en: Download Excel-file
config:
targetApp: transformations
valuesParams:
limit: 1000000
executionParams:
type: export-xlsx
timeout: T1H
config:
fileName: "${$context.journalName}_${$now|fmt('yyyy-MM-dd_HH-mm-ss')}"
columns: "${$context.reportColumns[]?json}"
outputParams:
type: EMAIL
config:
notificationRef: notifications/template@default-link-for-export-file-notification
features:
execForQuery: true
execForRecord: false
execForRecords: true