Serge Huber created UNOMI-964:
---------------------------------

             Summary: Flaky GraphQLListIT.testCRUD: stale static 
condition-factory singletons produce match-none profile queries
                 Key: UNOMI-964
                 URL: https://issues.apache.org/jira/browse/UNOMI-964
             Project: Apache Unomi
          Issue Type: Bug
          Components: unomi(-core)
    Affects Versions: unomi-2.7.0, unomi-3.0.0, unomi-3.1.0
            Reporter: Serge Huber
            Assignee: Serge Huber
             Fix For: unomi-3.1.0


h2. Symptom

{{org.apache.unomi.itests.graphql.GraphQLListIT.testCRUD}} fails 
intermittently, both locally and in CI, with:

{noformat}
java.lang.AssertionError: Failed waiting for profile in list query
    at org.apache.unomi.itests.BaseIT.keepTrying(BaseIT.java:848)
    at 
org.apache.unomi.itests.graphql.GraphQLListIT.testCRUD(GraphQLListIT.java:83)
{noformat}

The test retries ~30 times over ~60s and never sees the profile as a member of 
the list. Re-running the job usually passes.

h2. Root cause

The GraphQL condition factories ({{ProfileConditionFactory}}, 
{{EventConditionFactory}}, {{ProfileAliasConditionFactory}}, 
{{TopicConditionFactory}}) are lazy *static singletons*. The base 
{{ConditionFactory}} snapshots {{definitionsService.getAllConditionTypes()}} 
once into {{conditionTypesMap}} and never refreshes it.

Because {{instance}} is {{static}}, that snapshot is frozen for the entire 
PaxExam {{@PerSuite}} container lifetime, captured under whatever tenant/warmup 
context triggered the *first* {{get()}} call in the suite. If that first call 
happens before the condition-type cache is fully warm, 
{{getConditionType("profilePropertyCondition")}} returns {{null}}, producing a 
{{Condition}} with a null type. The ES dispatcher then logs and short-circuits:

{noformat}
TypeResolutionServiceImpl: Condition has no type ID for query builder
ConditionESQueryBuilderDispatcher: Condition type is null for condition 
typeID=null, returning match-none query
{noformat}

So the {{findLists.active}} member query returns zero rows for the rest of the 
run.

This explains every observed detail:
* {{totalCount == 1}} still passes because the list-metadata query uses a 
*fresh* {{ConditionFactory}}, while {{active.edges}} is always empty because 
the member query uses the *stale singleton*.
* It is a structural {{match-none}}, not an indexing delay — which is why prior 
timing-based fixes (deferring {{refreshPersistence}}, tripling retries in 
commit {{9eeac6d21}}) never worked.
* Intermittency depends purely on suite/test ordering and warmup timing at 
first singleton initialization; a fresh container (re-run) usually initializes 
it at a "good" time.

h2. Scope of fix

# Remove the static singletons from the four factories; return a fresh instance 
per {{get()}}. (A local stash already implements this.)
# In the same PR, drop the {{conditionTypesMap}} snapshot in 
{{ConditionFactory}} and resolve types live via 
{{definitionsService.getConditionType(id)}}. This also removes the leaked 
first-request {{DataFetchingEnvironment}}/{{ServiceManager}}.
# Harden {{AddProfileToListCommand}}: replace {{send(event) == 
EventService.PROFILE_UPDATED}} with {{(send(event) & PROFILE_UPDATED) == 
PROFILE_UPDATED}} (latent bitmask bug on the same code path).
# Revert the misleading timing workarounds and comment in 
{{GraphQLListIT.testCRUD}}.

h2. Performance

Negligible. All condition-type lookups are in-memory cache reads (no 
Elasticsearch/IO). Net-positive: removes a {{synchronized}} static bottleneck 
and a per-container memory retention. The high-frequency ingestion path 
({{/context.json}}, {{/eventcollector}}, rule/segment evaluation) does not use 
these factories and is unaffected.

h2. Acceptance criteria

* {{GraphQLListIT.testCRUD}} passes reliably (e.g. 20 consecutive local runs on 
the Elasticsearch profile).
* No recurring {{returning match-none query}} warnings during the run.
* Full {{graphql}} IT package is green.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to