Add CollaborativeEditing::DocumentStore and prepare rate limit

What does this MR do and why?

Adds Gitlab::CollaborativeEditing::DocumentStore, which holds the Yjs update log for one collaboratively edited document in Gitlab::Redis::SharedState.

Updates are opaque base64 blobs. The server never decodes or merges them. Yjs updates are commutative and idempotent, so a client that applies the whole log in any order converges on the same document. Keeping CRDT logic out of Ruby is deliberate — an earlier attempt used Rust bindings for a Ruby-side CRDT and proved unsustainable to maintain.

The log is ephemeral. It lets a late joiner catch up and survives all clients briefly disconnecting. The durable copy of a wiki page is still the Git commit written on save.

Public methods:

  • #updateslrange the whole log.
  • #appendrpush and refresh expiry. Returns true once the log hits COMPACTION_THRESHOLD, so the caller can ask a client for a snapshot.
  • #replacedel plus rpush of a compacted snapshot.
  • #claim_seed — Redis SET NX with a TTL, electing one client to write the initial document.

TTL is 1 hour, refreshed on every write, so a document expires only once editing stops. COMPACTION_THRESHOLD is 500. SEED_CLAIM_TTL is 30 seconds. Keys are hash-tagged (collaborative_editing:{<document_key>}:updates and :seed) to pin both keys for a document to one Redis Cluster slot.

Also registers a collaborative_editing_update rate limit: 600 per minute, characteristics [user, document_key]. Keying on the user and document in Redis, rather than on a channel instance, stops a user multiplying their allowance by opening extra subscriptions to the same document. It has no call site — the channel that consumes it lands in slice 3, so slice 3 stays purely the channel.

Nothing here is reachable by a user. No channel, no feature flag, no caller. DocumentStore is not instantiated anywhere yet.

Part of a split

Slice 2 of 6, split out of !250607 (closed) at this review comment.

  • Slice 1 (merged): !251691 (merged) — optional collaborationProvider threaded into ContentEditor.
  • Slice 3: CollaborativeEditing::BaseChannel and WikiPageChannel — AppSec review goes here.
  • Slice 4: the ActionCableProvider implementation.
  • Slice 5: wiki wiring, CollaboratorsIndicator, and the feature flag that makes the feature reachable.

Testing

  • 10 new examples in spec/lib/gitlab/collaborative_editing/document_store_spec.rb.
  • 23 existing examples in supported_rate_limits_spec.rb still pass.
  • 118 existing examples across application_rate_limiter_spec.rb and labkit_adapter_spec.rb still pass.
  • RuboCop clean on all three files.

No manual validation steps: nothing in this MR is reachable by a user.

References

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.


🤖 This content was generated by GitLab Duo.

Edited by Lee Tickett

Merge request reports

Loading
Loading