Unomi 3.1 · Operations
Cluster-aware task scheduler
Durable background work with persistence, locks, retry/resume, REST, shell, and a plugin-friendly fluent API.
Unomi 3.1 introduces a first-class task scheduler for work that must run reliably on a single node or across a cluster: maintenance timers, request offload (for example profile-merge follow-up), and operator-visible retry/cancel.
It sits on the persistence-based clustering model — executor nodes and locks use the shared search index rather than Cellar or Hazelcast.
Capabilities at a glance
- Task types + executors — each logical
taskTypeis handled by a registeredTaskExecutor(core services or plugins) - Persistent or in-memory — cluster-visible
scheduledTaskitems in Elasticsearch/OpenSearch, ornonPersistent()node-local tasks (typical for cache refresh) - One-shot and recurring —
asOneShot(), or period +withFixedDelay()/withFixedRate(), with optionalwithInitialDelay - Cluster coordination — executor nodes (
scheduler.executorNode), cluster locks, optionalrunOnAllNodes(), parallel execution control viadisallowParallelExecution() - Lifecycle — statuses
SCHEDULED,WAITING,RUNNING,COMPLETED,FAILED,CANCELLED,CRASHED - Retries —
withMaxRetries/withRetryDelay(auto); manual retry via REST/shell (optional failure-count reset) - Dependencies —
withDependencies(taskId…)so a task waits until prior work isCOMPLETED - Checkpoints / resume — long jobs save
checkpointData/currentStep; operators resumeCRASHEDtasks when the executor implementscanResume/resume - Parameters — JSON-serializable map passed into the executor (include
tenantIdand run underexecuteAsTenantfor tenant isolation) - System tasks —
asSystemTask()for jobs created at init that should be preserved across restarts - Fluent API —
schedulerService.newTask(type)builder; alsocreateRecurringTask/createTaskhelpers - Plugin registration — register/unregister
TaskExecutorfrom OSGi DSactivate/deactivate - REST ops — list (filter by status/type, pagination), get, cancel, retry, resume (admin / tenant-admin); task creation stays in Java services/plugins
- Karaf shell —
unomi:task-list|show|cancel|retry|purge|executor - Built-in purge — periodic
task-purgeof oldCOMPLETEDrecords (scheduler.purgeTaskEnabled, TTL days) - Metrics —
getMetric/getAllMetrics(completed, failed, crashed, running, lock conflicts, recovery attempts, …) - Config — thread pool size, stable
scheduler.nodeId, lock timeout, executor vs API-only node roles inorg.apache.unomi.services.cfg
REST surface
Base path /cxs/tasks (roles ROLE_UNOMI_ADMIN / ROLE_UNOMI_TENANT_ADMIN) — monitoring and operations, not create:
GET /cxs/tasks— paginated list; filtersstatus=,type=,offset,limitGET /cxs/tasks/{id}— detail (parameters, checkpoint, errors)DELETE /cxs/tasks/{id}— cancel (HTTP 204; record kept asCANCELLED)POST /cxs/tasks/{id}/retry— optional?resetFailureCount=truePOST /cxs/tasks/{id}/resume— forCRASHED+ checkpoint-capable executors
Karaf shell
unomi:task-list [-s STATUS] [-t TYPE] [--limit N]
unomi:task-show <id>
unomi:task-cancel <id>
unomi:task-retry <id> [--reset]
unomi:task-purge [--days N] [--force]
unomi:task-executor # is this node an executor? nodeId?
Fluent scheduling (plugins / services)
schedulerService.newTask("cache-refresh-segment")
.nonPersistent()
.withPeriod(1, TimeUnit.SECONDS)
.withFixedDelay()
.disallowParallelExecution()
.withSimpleExecutor(() -> refreshSegmentCache())
.schedule();
schedulerService.newTask("export-tenant-snapshot")
.withParameters(Map.of("tenantId", "acme"))
.asOneShot()
.withMaxRetries(3)
.withRetryDelay(2, TimeUnit.MINUTES)
.disallowParallelExecution()
.withExecutor(myExportExecutor)
.schedule();
Built-in task types you will see
cache-refresh-*— reload segments, rules, property types, scopes, … into memoryprofile-purgesegment-date-recalculationrules-statistics-refreshtask-purge— purge old completed scheduler tasksmerge-profiles-reassign-data— async session/event reassignment after profile merge (baseplugin)clusterNodeStatisticsUpdate/clusterStaleNodesCleanup— clustering maintenance
Async enrichment-style jobs can reuse the same model instead of ad-hoc threads — register an executor, schedule with the builder, operate via REST/shell.