Skip to content

Server: ISubscription publishes 11 members of internal publish-pipeline protocol on its public interface #4185

Description

@marcschier

Summary

ISubscription has 42 members. Eleven of them are publish-pipeline collaboration protocol with SubscriptionManager and SessionPublishQueue — internal machinery published on a public interface, where any holder of an ISubscription can call them and corrupt the publishing state machine.

Evidence

Subscription/ISubscription.cs — 42 members over Subscription (3,471 lines).

The 11 protocol members:

Member Purpose
ItemReadyToPublish monitored item signals data available
ItemNotificationsAvailable monitored item signals queued notifications
QueueOverflowHandler queue overflow callback
PublishTimerExpired publish timer tick
PublishTimeout timeout notification message
SubscriptionTransferred transfer notification message
AvailableSequenceNumbersForRetransmission retransmission bookkeeping
Acknowledge sequence number acknowledgement
ResendData resend request
SessionClosed() session teardown
SessionClosed(ISession) session teardown, overload

Why this matters

These are called by exactly two collaborators — SubscriptionManager and SessionPublishQueue — but they sit on the public interface. A node manager reaching through IServerInternal.SubscriptionManager.GetSubscriptions() can call PublishTimerExpired() or QueueOverflowHandler() directly and drive the publishing state machine into an inconsistent state. Nothing in the type system prevents it.

The interface is also the test surface, so tests of subscription behaviour are invited to drive the protocol directly rather than asserting observable outcomes.

The behaviour behind the seam is real

This is not a pass-through — the publishing machinery is substantial:

Module Lines
Subscription.cs 3,471
SessionPublishQueue.cs 1,071
SentMessageQueue.cs 449
Total ~5,000

Deletion test

Delete the 11 members from the public interface: complexity reappears, but only inside SubscriptionManager and SessionPublishQueue, which are the only legitimate callers. That is relocation into an internal seam, not duplication across callers.

Proposed direction

Move the publish-pipeline protocol to an internal seam consumed only by SubscriptionManager and SessionPublishQueue. Leave the service-set operations (CreateMonitoredItemsAsync, ModifyMonitoredItemsAsync, DeleteMonitoredItemsAsync, SetMonitoringModeAsync, SetPublishingMode, Publish, Republish, Modify, ConditionRefreshAsync, SetTriggering, TransferSessionAsync, …) on ISubscription.

Expected result: 42 → ~25 members, no behaviour change, no plugin-API break.

Open question

Does durable-subscription restore require external access to any of the 11? ToStorableSubscription(), SetSubscriptionDurable() and AvailableSequenceNumbersForRetransmission() interact with the persistence path (Subscription/Persistence/), so the internal seam must still serve ISubscriptionStore and ISubscriptionRetransmissionStore. See docs/DurableSubscription.md.

Constraints

  • 1.5.378 compatibility — additive; mark the moved members [Obsolete] on ISubscription and forward to the internal seam during transition.
  • Must continue to support TransferSubscriptions (docs/TransferSubscription.md) and durable subscriptions.
  • Tests that currently drive the protocol directly should be replaced with tests asserting observable outcomes through the service-set operations — replace, don't layer.

Dependency category

In-process. Pure computation and in-memory state; no port required. The persistence seams (ISubscriptionStore, IMonitoredItemQueueFactory, ISubscriptionRetransmissionStore) already exist and are reused unchanged.

Verified against commit e73e71184.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions