Thanks for the review Jonathan,

I will cancel the release, create JIRA issues for these points and
then fix all of them.

Best regards,
  Serge...

On Mon, Oct 5, 2026 at 5:48 PM Jonathan Sinovassin-Naïk via dev
<[email protected]> wrote:
>
> Hi Serge,
>
> -1
>
> Thank you for the work on this release. The release mechanics are correct,
> but the migration from 3.0.x fails on real 3.0.1 data, and several
> regressions affect existing features.
>
> I compared each item below with the unomi-root-3.0.1 tag, and all of them
> are new in 4.0.0.
>
> What I checked, and what passed:
>
> - The sha512 checksums and the GPG signatures of the 3 archives are
> correct. The key 16DF 7E56 7738 172F 945F 0402 7C19 D1FB BD1A B1DF is in
> KEYS.
> - The source zip matches the unomi-root-4.0.0 tag, apart from the files
> that Maven generates or that git ignores. The staging source-release zip is
> identical to the dist one.
> - RAT reports 0 unapproved files.
> - The build from the source zip with JDK 17 and Maven 3.9.11 succeeds, with
> 1681 unit tests and 0 failures.
>
>
> BLOCKING ISSUES
>
> 1. Migration from 3.0.x
>
> I ran the procedure of migrate-3.0-to-4.0.adoc on a Unomi 3.0.1 data set,
> on Elasticsearch 9.4.3, with the binary from dist/dev.
>
> a. unomi:migrate fails at the first reindex with "no mapping found in
> bundles for: event.json". Before unomi:start, the only Unomi features
> installed are unomi-base and unomi-startup, so
> MigrationUtils.extractMappingFromBundles (MigrationUtils.java:305) finds no
> mapping file. The mappings of unomi-persistence-elasticsearch-core and
> cxs-geonames-services are both missing. In 3.0.1, the boot feature
> unomi-kar installed these two bundles together with shell-commands.
> Migrate16xToCurrentVersionIT cannot detect this, because Pax Exam installs
> unomi-elasticsearch-core and the other features before the migration runs.
>
> b. An index with no known mapping stops the migration with "no mapping
> found in bundles for: generic.json"
> (migrate-4.0.0-01-tenantDocumentIds.groovy:82,136). A 3.0.1 cluster always
> has a context-clusternode index. A cluster that saved a Salesforce
> connector configuration also has context-sfdcconfiguration, which takes the
> same path.
>
> c. The built-in definitions of 3.0.1 hide the built-in definitions of
> 4.0.0. The script initialize_tenant_and_audit_fields.painless:22,95 tests
> scope == 'system', but no 3.0.1 item has this value. In 3.0.1, rules,
> segments, scorings and goals keep the scope "systemscope" under
> metadata.scope. The built-in condition types, action types and property
> types have no scope. All the 3.x items go to the "default" tenant, and the
> copy in that tenant wins over the copy in "system"
> (MultiTypeCacheServiceImpl.java:126-139, RulesServiceImpl.java:281-304).
>
>    After the migration, the built-in definitions that changed in 4.0.0 keep
> their 3.0.1 form, such as sessionAssigned, pastEventCondition,
> profilePropertyCondition and booleanCondition. For example, a new visit
> increments nbOfVisits but not totalNbOfVisits.
>
> d. migrate-4.0.0-05-fixSystemItemIds.groovy:60 uses a term query on the
> analysed field itemType. The camelCase types, such as conditionType,
> actionType and propertyType, match nothing, and the log still says "Fixed".
>
> e. migrate-4.0.0-10-tenantInitialization.groovy:109,112 writes a key file
> that names the headers X-Unomi-Public-Key and X-Unomi-Key.
> AuthenticationFilter.java:69 reads the header X-Unomi-Api-Key.
>
> f. The tests on -systemitems and -geonames in the painless script are
> always false. During the reindex, ctx._index is "<index>-cloned", and the
> geonames index is "geonameentry". The migrated geonames entries go to
> "default", but GeonamesServiceImpl reads them as "system".
>
> g. The migration tests do not detect the issues above.
> Migrate16xToCurrentVersionIT has 4.0.0 checks, and MigrationIT tests the
> resume with synthetic scripts. But the 4.0.0 checks have these limits:
>
>   - The IT restores a 1.6.x snapshot and runs "unomi:migrate 1.6.0 true",
> so no test starts from 3.x data. The snapshot has no context-clusternode
> index.
>   - Pax Exam installs unomi-elasticsearch-core before the migration runs,
> so the missing mapping bundles of item a do not fail the test.
>   - checkDefinitionsServiceObjects accepts a "system_" prefix or a test
> tenant prefix. It does not check that the 4.0.0 version of a built-in
> definition is the one in use, which is item c.
>   - checkDefaultTenantCreated checks that the tenant index exists, but it
> makes no assertion when tenantService.getTenant() returns null and the
> search finds no tenant document.
>   - checkMigratedData runs only on Elasticsearch.
>
> I reproduced a to e. I found f in the code and did not reproduce it.
>
> 2. ElasticSearchPersistenceServiceImpl.purge(scope) deletes documents of
> other scopes
>
> At ElasticSearchPersistenceServiceImpl.java:2734, the call TermQuery.of(b
> -> b.field("scope").value(scope).field("tenantId").value(...)) keeps only
> the last field and value, because the builder overwrites them. The query
> has no scope filter. DELETE /cxs/cluster/{scope} therefore deletes
> documents of any type of the tenant, in all the indices with the index
> prefix. In 3.0.1, the query filtered on the scope. The OpenSearch
> implementation filters on the scope and on the tenant.
>
> 3. Scheduled tasks run only for the "system" tenant
>
> Inside executeAsSystem, the persistence layer adds a filter on tenantId
> "system" (ElasticSearchPersistenceServiceImpl.java:2982-3000). These tasks
> therefore see only the "system" tenant:
>
> - The profile-purge task (ProfileServiceImpl.java:363-376) runs
> purgeProfiles, purgeSessionItems and purgeEventItems inside
> executeAsSystem. The settings org.apache.unomi.profile.purge.*,
> org.apache.unomi.session.purge.existTime and
> org.apache.unomi.event.purge.existTime have no effect on the data of
> "default" or of any other tenant.
> - The daily recalculation of segments and scorings with relative dates
> reads the cache of the "system" tenant (SegmentServiceImpl.java:1252-1290
> and 1553). A tenant segment such as "visited in the last 7 days" is never
> evaluated again.
> - After a restart, initCamel runs with no execution context, so the
> persistence layer uses the "system" tenant. The recurrent import and export
> configurations of the tenants are not scheduled
> (ProfileImportFromSourceRouteBuilder.java:104,
> RouterCamelContext.java:317). A configuration comes back only when someone
> saves it again.
>
> RulesServiceImpl and TenantUsageServiceImpl loop over the tenants with
> executeAsTenant, but these three tasks do not.
>
> 4. Single-tenant compatibility mode
>
> - On a public path, the mode always creates a guest subject
> (AuthenticationFilter.java:311). The mode still checks X-Unomi-Peer and the
> IP address to accept a protected event, but the header gives no role. A 3.0
> third-party server that sends X-Unomi-Peer loses these features:
>
>   - the profile merge on login,
>   - the profileId of the request body,
>   - the itemId of events,
>   - the cross-profile updateProperties, and the writes to systemProperties,
> segments, scores and consents.
>
>   The Javadoc of the method says "Protected events require IP +
> X-Unomi-Peer (like V2)". The 3.0.x backport of UNOMI-975 (commit 8824da595)
> keeps X-Unomi-Peer as the trusted caller for the profile binding.
> - Each public request calls TenantServiceImpl.getOrCreateTenant, which is
> synchronized (TenantServiceImpl.java:117). The method reads the tenant from
> the persistence layer with no cache (TenantServiceImpl.java:213-214), so
> all the context.json requests go through one JVM lock and one Elasticsearch
> round trip. Each public request already read the tenant with no cache
> before my change for UNOMI-980 (https://github.com/apache/unomi/pull/861).
> That change added the lock, and I can propose a fix.
>
> OTHER REGRESSIONS
>
> - GET /cxs/query/{type}/{property}, and POST on the same path with no
> condition, count the data of all the tenants. The persistence layer adds
> the tenant filter only when the request carries a condition
> (ElasticSearchPersistenceServiceImpl.java:2500-2513). POST
> /cxs/query/{type}/{property}/{metricTypes} never filters on the tenant
> (getSingleValuesMetrics, line 2780). OpenSearch behaves the same way.
> - Segment and scoring lists ignore the sort. SegmentServiceImpl merges the
> sorted results into a HashMap, and the sort is a TODO
> (SegmentServiceImpl.java:352, 1637 and 1720). In POST /cxs/segments/query
> and POST /cxs/scoring/query, a missing limit or a limit of -1 gives a
> negative toIndex to subList (line 1732). When at least one item matches,
> the call answers 400 instead of the list.
> - hoverEventCondition with only targetId matches every hover event, because
> since UNOMI-828 the condition is an OR of targetId and targetPath. My
> change for UNOMI-982 (https://github.com/apache/unomi/pull/864) makes a
> branch with an absent parameter evaluate to true. The OR is therefore
> always true when one of the two parameters is missing.
> - context.json, eventcollector and client now set allowCredentials=false (
> https://github.com/apache/unomi/pull/878). The shipped tracker sends
> withCredentials=true, so a browser blocks the response on a cross-origin
> setup.
> - Unomi no longer reads UNOMI_HEALTHCHECK_ENABLED,
> UNOMI_HEALTHCHECK_PROVIDERS and UNOMI_HEALTHCHECK_TIMEOUT. Commit cef067f2d
> (UNOMI-919) removed their mapping from custom.system.properties.
> configuration.adoc:1856-1862, index.adoc:180-181 and the quickstart
> (5-min-quickstart.adoc:98) still document them.
> - scheduler.nodeId defaults to "test-scheduler-node", and no environment
> variable sets it. TaskLockManager compares the lock owner with this id
> (TaskLockManager.java:240). Two nodes that keep the default can therefore
> both take the lock and run the same task.
>
>
> UNDOCUMENTED BREAKING CHANGES
>
> migrate-3.0-to-4.0.adoc covers the API keys, the cookie, the mandatory
> passwords, the conditions with no type, the router file paths and the
> privileged REST roles. The guide does not document these changes:
>
> - An admin call with Basic auth and no X-Unomi-Tenant-Id header runs in the
> "system" tenant. It does not see the data that the migration put in
> "default". The guide shows this call at lines 125-130. security.adoc calls
> the header optional, but no page says what happens without it. An unknown
> tenant id also falls back to "system" with only a warning. But
> how-profile-tracking-works.adoc:126-128 says that the tenant must exist,
> and that the request fails with 401.
> - X-Unomi-Peer and thirdparty.cfg apply only in compatibility mode.
> recipes.adoc and builtin-event-types.adoc say this for X-Unomi-Peer, but
> configuration.adoc:165-222 still presents the thirdparty settings as
> active. No page documents the tenant fields restrictedEventTypes and
> authorizedIPs, which now control the protected events.
> - These Java API changes are not mentioned:
>   - SchedulerService.getScheduleExecutorService() and
> getSharedScheduleExecutorService() are removed.
>   - EventService.authenticateThirdPartyServer and isEventAllowed are
> removed, and isEventAllowedForTenant replaces them.
>   - Parameter.getDefaultValue() now returns Object, and setDefaultValue now
> takes an Object, which breaks binary compatibility.
>   - DefinitionsService.extractConditionsByType now returns a List instead
> of a Set.
> - The empty unomi-plugins-past-event artifact is removed.
> PastEventConditionEvaluator and SourceEventPropertyConditionEvaluator moved
> from unomi-plugins-baseplugin to the new unomi-plugins-advanced-conditions,
> in the package org.apache.unomi.plugins.advancedconditions.conditions.
> HoverEventConditionESQueryBuilder is removed.
> - 18 shell commands are removed in favour of unomi:crud, among them
> profile-list, rule-list and segment-view. shell-commands.adoc:99-189 still
> lists them, and tutorial.adoc:226 and 274 still use them.
> - Document ids now start with the tenant id: <tenantId>_<itemId>, or
> <tenantId>_<itemId>_<itemType> for the system items. This change affects
> any script or dashboard that reads _id.
> - UNOMI_GRAPHQL_FEATURE_ACTIVATED is removed, and only the graphql
> distributions or the cdp-graphql-feature enable GraphQL. In the health
> check, org.apache.unomi.healthcheck.cfg becomes
> org.apache.unomi.healthcheck-elasticsearch.cfg, and
> httpClient.trustAllCertificates becomes esHttpClient.trustAllCertificates.
> configuration.adoc still names the old file. POST /cxs/profiles with no
> itemId now answers 400.
> - condition-validation.adoc:187 says that the REST client receives the
> validation message. The client receives {"errorMessage":"badRequest"}.
>
> MINOR, NOT BLOCKING
>
> The staging repository holds unomi-4.0.0-buildcache.jar. Unomi 3.0.1 did
> not publish this artifact.
>
> Regards,
> Jonathan
>
> On Sun, Oct 4, 2026 at 4:19 PM Mohamed-Tayeb BENTERKI <[email protected]>
> wrote:
>
> > +1 🚀
> >
> > On Sun, 4 Oct 2026 at 14:18, Serge Huber <[email protected]> wrote:
> >
> > > Hi all,
> > >
> > > I (finally !) submit Apache Unomi 4.0.0 to your vote.
> > >
> > > This release has been a long time coming. The work began as the "Unomi
> > > Version 3" plan (UNOMI-875, February 2025), with implementation from
> > March
> > > 2025 — months before Unomi 3.0.0 was cut in November 2025. By September
> > > 2025 the tree was already versioned 3.1.0-SNAPSHOT; 3.0.0 (and later
> > > 3.0.1 in July 2026) was a first slice, while this line continued in
> > > parallel. As the scope grew — multi-tenancy, a second persistence
> > backend,
> > > a large platform refresh, and a much stronger REST and operations story —
> > > we renamed it 4.0.0 so the version number matches the size of the change.
> > > This candidate is the result.
> > >
> > > A short view of what 4.0 brings:
> > >
> > > Features
> > > - Multi-tenancy, with a compatibility mode for single-tenant setups
> > > - OpenSearch as a persistence option alongside Elasticsearch
> > > - Cluster-aware scheduling, unified tenant caching, and a unified shell
> > >   CRUD
> > > - Tenant usage through the REST API, plus event retention purge
> > > - Request tracing, and a rangeQuery API on persistence
> > > - REST V2 compatibility mode, better input validation, and clearer error
> > >   responses
> > > - Migration scripts from 3.x, including resume-safe 3.1/4.0 migration
> > >
> > > Platform and libraries
> > > - Newer Karaf, Elasticsearch, OpenSearch, and Log4j
> > > - Further third-party updates (UNOMI-990 and related)
> > > - Stricter startup (explicit admin and health-check passwords) and
> > tighter
> > >   control of privileged REST, Groovy actions, and import/export
> > > - Build cache, BOM coverage for advanced conditions, and a published
> > >   distribution again
> > >
> > > Fixes
> > > - Condition validation and matching (optional parameters, unresolved
> > >   types, segment/scoring save)
> > > - Context/profile binding, CSV import, persona sessions, health-check
> > >   concurrency
> > > - Scheduler locks and recovery in a cluster
> > > - Tracker WAB, visit/session counts, logging, and several CI/IT flakes
> > >
> > > Documentation and developer experience
> > > - Manual refresh with diagrams, Javadoc, and OpenAPI filled from Javadoc
> > > - Pluggable persistence ITs, better IT progress/tooling, improved
> > build.sh,
> > >   and contributor guidelines
> > >
> > > Tests grew a lot compared with 3.0.1. Annotated test methods went from
> > > about
> > > 280 to about 2,130. Most of that is new unit tests: roughly 90 unit tests
> > > in
> > > 15 classes became about 1,640 in 120 classes, while integration tests
> > rose
> > > from about 190 to about 490. The bulk of the unit work uses a new
> > in-memory
> > > persistence manager (UNOMI-881), so core services can be tested without
> > > Elasticsearch or OpenSearch. That harness also has its own suite (about
> > 200
> > > tests on the in-memory store), and more than a dozen service tests
> > > (profiles,
> > > events, rules, segments, scheduler, conditions, and others) now run on
> > it.
> > >
> > > Full JIRA notes (about 79 issues):
> > >
> > >
> > https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12319220&version=12356384
> > >
> > > Staging repository:
> > > https://repository.apache.org/content/repositories/orgapacheunomi-1054/
> > >
> > > Source:
> > >
> > >
> > https://repository.apache.org/content/repositories/orgapacheunomi-1054/org/apache/unomi/unomi-root/4.0.0/unomi-root-4.0.0-source-release.zip
> > >
> > > Convenience binaries:
> > >
> > >
> > https://repository.apache.org/content/repositories/orgapacheunomi-1054/org/apache/unomi/unomi/4.0.0/
> > >
> > > Git tag:
> > > unomi-root-4.0.0
> > >
> > > Dist:
> > > https://dist.apache.org/repos/dist/dev/unomi/4.0.0/
> > >
> > > Please vote to approve this release:
> > >
> > > [ ] +1 Approve the release
> > > [ ] -1 Don't approve the release (please provide specific comments)
> > >
> > > This vote will be open for at least 72 hours, or until the necessary
> > > number of binding votes (3 +1) is reached.
> > >
> > > Please let me know if you have any questions.
> > >
> > > Thanks,
> > > Serge
> > >
> >
>
>
> --
> [image: Dev]
>
>
> *Jonathan SINOVASSIN NAIK*FULLSTACK DEVELOPPER
> [image: linkedin]
> <https://app.signitic.com/l/ACdRwwAAABAAAAAABhZkJQ.t7MdoRPQji8> [image: web]
> <https://app.signitic.com/l/ACdRwwAAABYAAAAABhZkJQ.8yvongia6Lk> [image:
> github]
> <https://app.signitic.com/l/ZGhoWnNIeVJXV051NzBOYmZMMTFaUT09-NlpUZ01IaXcvRTNYWnk5bEtRWEZCdz09>
>
> [image: Bannière Jahia avec badges G2 - Leader - Best Relationship - Best
> Results - Best Usability]
> <https://app.signitic.com/l/ACdRwwAAABUAAo7LBhZkJQ.r8W_TAY9ofY>

Reply via email to