Context
PR #829 introduced a cleaner event stream pattern for ArkService.GetEventStream:
- Client opens one stream connection
- Server immediately emits
StreamStartedEvent with a stream ID
- Client uses
UpdateStreamTopics (unary RPC) to dynamically add/remove/overwrite topics on that stream
- Result: one connection to manage instead of juggling subscribe-then-stream choreography
The IndexerService in indexer.proto currently uses an older multi-step pattern:
- Client calls
SubscribeForScripts (unary) → gets back a subscription_id
- Client opens
GetSubscription(subscription_id) (server stream) → receives events
- To modify scripts mid-stream, client calls
SubscribeForScripts again with the same subscription_id to add, or UnsubscribeForScripts to remove
This requires the client to manage the lifecycle of subscription creation before it can open the stream, and uses two separate unary RPCs for what is conceptually add/remove.
Proposal
Align IndexerService with the ArkService pattern from #829 — no breaking changes to existing RPCs.
1. Make subscription_id optional in GetSubscriptionRequest
message GetSubscriptionRequest {
// If empty, server creates a new subscription automatically.
// Existing clients can still pass a pre-created subscription_id.
string subscription_id = 1;
// Optional: scripts to subscribe to on stream creation.
// Ignored if subscription_id already has scripts.
repeated string scripts = 2;
}
When subscription_id is empty, the server creates a subscription internally, just like GetEventStream creates a listener. This lets new clients skip the SubscribeForScripts bootstrapping call entirely.
The optional scripts field lets the client subscribe to an initial set of scripts in the same call — single step to go from nothing to a fully active, filtered stream.
2. Add SubscriptionStartedEvent to the stream response
message GetSubscriptionResponse {
oneof data {
IndexerHeartbeat heartbeat = 1;
IndexerSubscriptionEvent event = 2;
SubscriptionStartedEvent subscription_started = 3; // NEW
}
}
message SubscriptionStartedEvent {
string subscription_id = 1;
}
Emitted immediately upon stream creation (mirrors StreamStartedEvent). The client now has the subscription ID without needing to call SubscribeForScripts first.
3. (Optional) Add UpdateSubscriptionScripts unary RPC
// Mirrors UpdateStreamTopics from ArkService
rpc UpdateSubscriptionScripts(UpdateSubscriptionScriptsRequest) returns (UpdateSubscriptionScriptsResponse) {
option (meshapi.gateway.http) = {
post: "/v1/indexer/script/updateScripts"
body: "*"
};
};
message UpdateSubscriptionScriptsRequest {
string subscription_id = 1;
oneof scripts_change {
ModifyScripts modify = 2;
OverwriteScripts overwrite = 3;
}
}
message ModifyScripts {
repeated string add_scripts = 1;
repeated string remove_scripts = 2;
}
message OverwriteScripts {
repeated string scripts = 1;
}
message UpdateSubscriptionScriptsResponse {
repeated string scripts_added = 1;
repeated string scripts_removed = 2;
repeated string all_scripts = 3;
}
This consolidates add/remove into a single RPC with oneof semantics, matching the UpdateStreamTopics pattern. The existing SubscribeForScripts and UnsubscribeForScripts RPCs continue to work unchanged.
Backward compatibility
| Client |
What changes? |
| Old clients |
Nothing. SubscribeForScripts → GetSubscription(id) → UnsubscribeForScripts all remain and work identically. |
| New clients |
GetSubscription() (no ID, optional initial scripts) → receive SubscriptionStartedEvent → UpdateSubscriptionScripts to modify. Single connection, cleaner lifecycle. |
Migration path
Once new clients have adopted the consolidated pattern, the old SubscribeForScripts / UnsubscribeForScripts RPCs can be deprecated (but not removed) in a future version.
Related
Context
PR #829 introduced a cleaner event stream pattern for
ArkService.GetEventStream:StreamStartedEventwith a stream IDUpdateStreamTopics(unary RPC) to dynamically add/remove/overwrite topics on that streamThe
IndexerServiceinindexer.protocurrently uses an older multi-step pattern:SubscribeForScripts(unary) → gets back asubscription_idGetSubscription(subscription_id)(server stream) → receives eventsSubscribeForScriptsagain with the samesubscription_idto add, orUnsubscribeForScriptsto removeThis requires the client to manage the lifecycle of subscription creation before it can open the stream, and uses two separate unary RPCs for what is conceptually add/remove.
Proposal
Align
IndexerServicewith theArkServicepattern from #829 — no breaking changes to existing RPCs.1. Make
subscription_idoptional inGetSubscriptionRequestWhen
subscription_idis empty, the server creates a subscription internally, just likeGetEventStreamcreates a listener. This lets new clients skip theSubscribeForScriptsbootstrapping call entirely.The optional
scriptsfield lets the client subscribe to an initial set of scripts in the same call — single step to go from nothing to a fully active, filtered stream.2. Add
SubscriptionStartedEventto the stream responseEmitted immediately upon stream creation (mirrors
StreamStartedEvent). The client now has the subscription ID without needing to callSubscribeForScriptsfirst.3. (Optional) Add
UpdateSubscriptionScriptsunary RPCThis consolidates add/remove into a single RPC with
oneofsemantics, matching theUpdateStreamTopicspattern. The existingSubscribeForScriptsandUnsubscribeForScriptsRPCs continue to work unchanged.Backward compatibility
SubscribeForScripts→GetSubscription(id)→UnsubscribeForScriptsall remain and work identically.GetSubscription()(no ID, optional initial scripts) → receiveSubscriptionStartedEvent→UpdateSubscriptionScriptsto modify. Single connection, cleaner lifecycle.Migration path
Once new clients have adopted the consolidated pattern, the old
SubscribeForScripts/UnsubscribeForScriptsRPCs can be deprecated (but not removed) in a future version.Related
ArkServiceevent stream consolidation