This venue's subscription lifecycle — internal. The facade's subscribe/2,
unsubscribe/2, update_symbols/2 and coverage/1 are served from here.
What a consumer can and cannot learn
A consumer learns what is arriving, through coverage/1. It cannot learn how: this
module owns the socket, the sharding and the pacing, and none of that reaches the
facade.
Coverage is observed, never intended
A symbol enters the coverage map when a payload for it arrives, never when it is subscribed. That distinction is the strongest guarantee in the contract and it exists because a venue once reported 325 symbols subscribed and confirmed while 174 were delivering. Reporting the subscription would have said 325.
A symbol that has been subscribed and has delivered nothing is simply absent, which the
facade documents as :not_covered.
Sharding
Coinbase carries its whole subscription on one connection. That is measured, not assumed: once its self-killing heartbeat was fixed it subscribed 401 of 401 pairs on a single socket, and sharding it anyway opened 14 connections for no gain.
So there is no shard arithmetic here. A venue that needs it — one whose socket stops accepting subscribes after about ten pairs — adds it in its package, where the measurement lives.
Summary
Functions
Returns a specification to start this module under a supervisor.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec coverage(GenServer.server()) :: %{ required(String.t()) => :stream | :internal_poll | :not_covered }
@spec start_link(keyword()) :: GenServer.on_start()
@spec subscribe(GenServer.server(), [String.t()], keyword()) :: :ok | {:error, term()}
@spec subscribe_notices( GenServer.server(), keyword() ) :: :ok
@spec unsubscribe(GenServer.server(), [String.t()]) :: :ok | {:error, term()}
@spec update_symbols(GenServer.server(), [String.t()]) :: :ok | {:error, term()}