Skip to content

Client: match subscriptions by context, not path only - #273

Open
t0susnik wants to merge 1 commit into
fair-acc:mainfrom
t0susnik:fix/client-demultiplex-subscriptions-by-channel-name
Open

Client: match subscriptions by context, not path only#273
t0susnik wants to merge 1 commit into
fair-acc:mainfrom
t0susnik:fix/client-demultiplex-subscriptions-by-channel-name

Conversation

@t0susnik

@t0susnik t0susnik commented Jul 28, 2026

Copy link
Copy Markdown

Fixes #272

SubscriptionMatcher can compare path and context, but the only way to select that mode was to pass
Filter classes to its constructor, which also registers them globally. A client that registers its
filters elsewhere could not enable it, so OpenCmwDataSource compared the URI path only and could
not tell apart concurrent subscriptions that differ only by their query.

This adds SubscriptionMatcher.pathAndContext() and uses it in OpenCmwDataSource. Nothing
domain-specific enters io.opencmw: the matcher discriminates on whichever context keys have a
Filter registered in FilterRegistry, and whoever owns a key registers it. On its own this change
is a no-op for a client that registers nothing. On our side a filter for channelNameFilter is
registered one layer up, which is what actually separates the subscriptions; the same approach covers
a second key we need without any further change here.

Behaviour change

Any query key present in the subscriber URI and registered in the global FilterRegistry is now
enforced when matching notifications; unregistered keys are still ignored. Because the registry is
global, filters registered elsewhere in the same JVM (e.g. TimingCtx via
DataSourcePublisher.checkClassForNewFilters()) are enforced too. That is the documented behaviour
of testPathAndContext, but it is new for this call site.

Note this is permanent for the process: there is no unregister, and nothing in core or client
calls FilterRegistry.clear(), so once a key is registered every context-mode matcher in the JVM
enforces it for the rest of the run. Registration itself is idempotent - registerNewFilter returns
early if all the classes are already known - so repeated calls are harmless. Two different Filter
implementations claiming the same key would silently collide though: both land in the class map, but
the key map keeps only the last registered, so the winner depends on class-initialisation order.

An observation on the constructor

SubscriptionMatcher(Class<? extends Filter>...) conflates two independent concerns: it selects the
matching mode and registers filters, with the mode derived from the argument count. It also does
not retain filterConfig - the only field is isPathOnly - so two matchers built with different
filter arguments behave identically, because matching consults the global registry. The argument list
therefore reads as if it scoped the matcher when it does not, and "context matching, with filters
registered elsewhere" is inexpressible, which is what this PR works around.

If you would rather make the mode explicit and deprecate the varargs form, say so and I will redo it
that way. I left it untouched because it is published API with tests against it, and reshaping it
does not belong in a bug fix.

Verification

Two concurrent subscriptions differing only by one context key against a C++ OpenCMW worker now
receive independent, correctly attributed streams for the full run; without the change one stops
receiving the instant the second registers. SubscriptionMatcherTest covers the new factory with a
filter registered via FilterRegistry rather than passed to the constructor.

@t0susnik
t0susnik marked this pull request as draft July 28, 2026 22:48
Add SubscriptionMatcher.pathAndContext() to select path-and-context matching
without naming Filter classes, and use it in OpenCmwDataSource. Notifications
are attributed to a subscription by the context keys whose Filter is registered
in FilterRegistry; keys without one are ignored.

Selecting context matching previously required passing Filter classes to the
constructor, which also registers them globally. A client that registers its
filters elsewhere had no way to enable it, so OpenCmwDataSource compared the
URI path only and could not tell apart concurrent subscriptions that differ
only by their query.
@t0susnik
t0susnik force-pushed the fix/client-demultiplex-subscriptions-by-channel-name branch from f187a9d to 7482d69 Compare July 29, 2026 07:41
@t0susnik t0susnik changed the title Client: differentiate subscriptions by the channelNameFilter context key Client: match subscriptions by context, not path only Jul 29, 2026
@t0susnik
t0susnik marked this pull request as ready for review July 29, 2026 08:02
@t0susnik

Copy link
Copy Markdown
Author

@RalphSteinhagen - flagging this for whenever you are back, no urgency. Fixes #272: a
small generic change to SubscriptionMatcher plus its use in OpenCmwDataSource.

There is a design question in the description about the varargs constructor conflating mode selection
with filter registration - I would value your view on whether you would rather make the mode explicit
and deprecate that form.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Client: only one of several subscriptions to the same service path receives data

1 participant