Skip to content

feat: add stream close reason to social service v2 subscription updates#439

Open
LautaroPetaccio wants to merge 2 commits into
mainfrom
feat/subscription-stream-close-reason
Open

feat: add stream close reason to social service v2 subscription updates#439
LautaroPetaccio wants to merge 2 commits into
mainfrom
feat/subscription-stream-close-reason

Conversation

@LautaroPetaccio

@LautaroPetaccio LautaroPetaccio commented Jul 10, 2026

Copy link
Copy Markdown

Problem

When the social service closes a subscription stream, the client only sees the stream end — the @dcl/rpc close-stream frame has no reason field, so there is no way to tell the client why.

Solution

Application-level close notice in the social service v2 protocol, sent as the final message of a stream:

  • New SubscriptionStreamClosed message: a SubscriptionStreamClosedReason plus an optional human-readable message for logging/debugging.
  • New optional SubscriptionStreamClosed stream_closed field on all six streamed update messages (FriendshipUpdate, FriendConnectivityUpdate, BlockUpdate, CommunityMemberConnectivityUpdate, PrivateVoiceChatUpdate, CommunityVoiceChatUpdate).

When stream_closed is present the message carries no update data and the stream ends right after it. Uniform across all six streams.

Reason enum — only what's deliverable

enum SubscriptionStreamClosedReason {
  STREAM_CLOSED_UNKNOWN = 0;
  STREAM_CLOSED_DUPLICATE_SUBSCRIPTION = 1;
}

A close notice can only reach the client while its connection is still alive (it travels as the final stream element, which the client must still be able to ACK). That holds for the duplicate-subscription rejection and essentially nothing else: shutdown and stale-cleanup closes happen after the socket is already gone, so those reasons could never be delivered. Rather than ship enum values that are dead on arrival, the enum keeps only UNKNOWN (proto3 zero default) and DUPLICATE_SUBSCRIPTION. It stays extensible — a future deliverable reason can be added.

Compatibility

  • New messages/enum/fields only — wire- and JSON-compatible, verified with buf breaking against main.
  • Old clients see a final message with an unknown field followed by a normal stream end.

Verification

  • make buf-lint, make buf-build, make buf-breaking pass.
  • Codegen + TypeScript compile (scripts/test.sh) pass.

🤖 Generated with Claude Code

Adds a SubscriptionStreamClosed message (reason enum + optional detail) and an
optional stream_closed field to all six streamed update messages, so the server
can send a final message informing the client why a subscription stream is
being closed. When stream_closed is present, the message carries no update data
and the stream ends right after it.

Wire- and JSON-compatible: new fields only (verified with buf breaking).
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

Test this pull request

  • The @dcl/protocol package can be tested in scenes by running
    npm install "https://sdk-team-cdn.decentraland.org/@dcl/protocol/branch//dcl-protocol-1.0.0-29103578374.commit-3c4b54e.tgz"

A close notice reaches the client as the final stream message, which requires
the connection to still be alive. That only holds for the duplicate-subscription
rejection; shutdown and stale-cleanup closes happen after the socket is already
gone, so those reasons could never be delivered. Trim the enum to UNKNOWN and
DUPLICATE_SUBSCRIPTION.

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: feat: add stream close reason to social service v2 subscription updates

Summary

Adds application-level close notices to the social service v2 streaming protocol. New SubscriptionStreamClosedReason enum (UNKNOWN=0, DUPLICATE_SUBSCRIPTION=1), SubscriptionStreamClosed message, and an optional stream_closed field on all 6 streamed update messages. Clean, well-documented, additive-only changes.

Architecture ✅

  • Design is sound. stream_closed placed as a standalone optional field outside existing oneof groups (e.g., outside oneof update in FriendshipUpdate). This is the correct protobuf pattern — it keeps backward compatibility and lets the server and client enforce mutual exclusivity by convention.
  • Enum discipline is good. Only listing deliverable reasons (UNKNOWN + DUPLICATE_SUBSCRIPTION) rather than dead-code enum values that can never reach the client is a pragmatic choice. The enum remains extensible.
  • Field numbers verified. All new field numbers are the next consecutive integer after existing fields — no conflicts:
    • FriendshipUpdate.stream_closed = 7 (after oneof 1-6)
    • FriendConnectivityUpdate.stream_closed = 3 (after 1-2)
    • BlockUpdate.stream_closed = 3 (after 1-2)
    • CommunityMemberConnectivityUpdate.stream_closed = 4 (after 1-3)
    • PrivateVoiceChatUpdate.stream_closed = 6 (after 1-5)
    • CommunityVoiceChatUpdate.stream_closed = 10 (after 1-9)

Backward Compatibility ✅

  • All changes are additive: new optional fields, new messages, new enum.
  • buf breaking passes against main.
  • Old clients (TS, C#, Rust) will silently ignore the unknown field on the wire — they see a zero-value update followed by a normal stream end, which is benign.

Consumer Impact ✅

Consumers found: social-service-ea (server, PR #432), unity-explorer (C#), godot-explorer (Rust), bevy-explorer (Rust, vendored), social-rpc-client-js (TS). All will gracefully degrade without code changes — they simply won't surface the close reason until they opt in. No breakage.

Security ✅

No security issues. The message field contains compile-time event names (public API names), not user input. No injection, auth bypass, or information disclosure vectors.

Style ✅

  • PR title follows feat: <summary> convention.
  • Branch name feat/subscription-stream-close-reason follows <type>/<summary>.
  • Proto comments are thorough and explain the contract clearly.

Verdict: APPROVE

No P0 or P1 findings. Well-designed, additive, backward-compatible protocol change with solid documentation.


Reviewed by Jarvis 🤖 · Requested by Lautaro Petaccio (<@U1140EA3W>) via Slack

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.

2 participants