Branch: refs/heads/XWIKI-24710 Home: https://github.com/xwiki/xwiki-platform Commit: f485f60d5c76a026faa7cafdb669c71bdb1ad3d5 https://github.com/xwiki/xwiki-platform/commit/f485f60d5c76a026faa7cafdb669c... Author: Vincent Massol <[email protected]> Date: 2026-08-26 (Wed, 26 Aug 2026) Changed paths: M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/pom.xml M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/ActiveInstallsConfiguration.java M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/DataManager.java A xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/TooManyExtensionsException.java M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/internal/DefaultActiveInstallsConfiguration.java M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/internal/DefaultDataManager.java M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/internal/data/DatePingDataProvider.java M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/internal/data/DistributionPingDataProvider.java M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/internal/data/ExtensionPingDataProvider.java M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/main/java/org/xwiki/activeinstalls2/script/ActiveInstallsScriptService.java M xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/test/it/org/xwiki/activeinstalls2/internal/PingSenderIT.java A xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/test/java/org/xwiki/activeinstalls2/ActiveInstallsConfigurationTest.java A xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/test/java/org/xwiki/activeinstalls2/DataManagerTest.java A xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/test/java/org/xwiki/activeinstalls2/internal/DefaultActiveInstallsConfigurationTest.java A xwiki-platform-core/xwiki-platform-activeinstalls2/xwiki-platform-activeinstalls2-api/src/test/java/org/xwiki/activeinstalls2/script/ActiveInstallsScriptServiceTest.java M xwiki-platform-tools/xwiki-platform-tool-configuration-resources/src/main/resources/xwiki.properties.vm Log Message: ----------- XWIKI-24710: Add script APIs to count distinct active installs globally and per extension * Add DataManager#countDistinctInstalls(), counting the distinct instances matching a query with a cardinality aggregation on the instance id. Contrary to countInstalls(), which counts pings, this counts installs: an instance pings once a day but also on every restart. * Add DataManager#countDistinctInstallsByExtension(), returning the same count per extension id in a single query. It uses a reverse nested aggregation since the extensions are indexed as a nested type while the instance id is on the root document. It returns a SequencedMap, so that the documented bucket ordering is part of the type rather than only of the Javadoc. * Expose both through the activeinstalls2 script service. * Both methods are default methods throwing their declared checked exception, so that existing DataManager implementations keep working. Mark them, and the two new configuration methods, @Unstable: the shape of the query slot and whether the per-extension count should be rights-gated are still open, and an unstable API can be reshaped within the cycle rather than deprecated. * Set the cardinality precision threshold explicitly on both aggregations: the maximum Elasticsearch supports (40000) for the global count, and the new activeinstalls2.extensionInstanceCountPrecisionThreshold property (10000 by default) for the per-extension one, which pays that memory once per extension counted. Document in both APIs the count above which the result is an approximation. * Add ActiveInstallsConfiguration#getMaxExtensionCountPerQuery(), configurable with the new activeinstalls2.maxExtensionCountPerQuery property, to bound how many extensions a single per-extension count can report. This makes both the point at which the query starts failing and the cost it can impose on the Elasticsearch cluster configurable. DefaultActiveInstallsConfiguration clamps both properties into the ranges the role documents. * Detect that a per-extension count would report only some of the extensions by asking the terms aggregation for one extension more than the maximum, and refusing the count when that extra one comes back, rather than by reading sum_other_doc_count. The former is exact whatever the number of shards of the index, while the latter also holds the buckets that a shard didn't report because they fell below its shard_size. * Raise the new TooManyExtensionsException rather than a generic Exception when that bound is exceeded, so that a caller can tell that raising the configured maximum is what's needed without matching on a message. It carries the maximum that was exceeded; the property to raise shapes its message but is not exposed, belonging to the ActiveInstallsConfiguration implementation in use rather than to the exception. * Assemble the queried field paths from the mapping property names declared by the ping data providers, instead of re-declaring them and keeping them in sync by hand. DatePingDataProvider now uses the shared instance id path too. * Document that a query on the extensions doesn't restrict the extensions being counted, that the counts are keyed by the id an extension is installed under rather than resolved through its features, and that restricting a count to the instances that are actually active is a matter of querying on the ping date. * Document on countInstalls() that it counts pings and not instances despite its name, and correct the field that its Javadoc example queries, which doesn't exist in the mapping. * Encode the JSON query in UTF-8 rather than in the platform default charset before base64-encoding it into the Elasticsearch wrapper query, and share the wrapper construction between countInstalls() and the search requests. * Extend the integration test to verify the distinct counts with and without a query, the per-extension counts and their order, that a maximum exactly reaching the number of extensions in the index still counts them all, and that exceeding it fails rather than under-reporting. Add unit tests for the DataManager and ActiveInstallsConfiguration default methods, for the script service and for the configuration, and raise the module's instruction coverage ratios to 0.92 with the integration tests and 0.12 without. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> To unsubscribe from these emails, change your notification settings at https://github.com/xwiki/xwiki-platform/settings/notifications