Skip to content

RCBC-558: Add Collection#get_replica - #257

Merged
avsej merged 1 commit into
couchbase:mainfrom
avsej:rcbc-558-get-replica
Oct 21, 2026
Merged

avsej merged 1 commit into
couchbase:mainfrom
avsej:rcbc-558-get-replica

Conversation

@avsej

@avsej avsej commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Implements RCBC-558: Collection#get_replica, defined by RFC-0053.

Problem

Collection#get_any_replica returns whichever copy answers first. An application cannot read one particular replica.

Change

strategy = Couchbase::GetReplicaStrategy.from_index(:second, wrap: true)
res = collection.get_replica("airline_10", strategy, Couchbase::Options::GetReplica(timeout: 2_000))
res.content["name"] #=> "40-Mile Air"
  • GetReplicaStrategy.from_index accepts :first, :second or :third. Any other index raises Error::InvalidArgument before a request is sent. So does a wrap that is not a Boolean.
  • Error::ReplicaIndexOutOfBounds: the bucket is configured with fewer replicas than the index asks for, e.g. :third with two.
  • Error::ReplicaIndexCurrentlyUnavailable: the bucket is configured with that replica, but no node holds it right now.
  • The SDK raises both errors from the vbucket map it already holds. Neither needs a network round trip.
  • The index is checked against the bucket's configured replica count, as the C++ core resolves it. During a rebalance, a vbucket whose replica chain is shorter reports Error::ReplicaIndexCurrentlyUnavailable.
  • Error::DocumentNotFoundOnReplica: the replica does not have the document. It is a subclass of Error::DocumentNotFound, so rescue Error::DocumentNotFound still catches it.
  • replica? is always true on the result. This read never goes to the active copy.
  • couchbase2:// raises Error::FeatureNotAvailable. The Couchbase gateway has no single-replica read.

With wrap: true, the read moves on instead of failing:

  • An index past the bucket's replica count wraps around. :third on a bucket with one replica reads the first replica.
  • A replica that no node holds is skipped for the next readable one.
  • A bucket without replicas still raises Error::ReplicaIndexOutOfBounds.
  • If no replica is readable, it still raises Error::ReplicaIndexCurrentlyUnavailable.

RFC-0053 specifies where the replica comes from:

the SDK will use the vbucket map from the most recent bucket config

Node health is not consulted:

  • A replica on a node that is down is still chosen until that node is failed over.
  • The read can then wait for that node until the read times out, with or without wrap.
  • A read that starts after the failover raises Error::ReplicaIndexCurrentlyUnavailable, or moves to the next replica with wrap.
  • The GetReplicaStrategy.from_index documentation shows how to bound the wait: a short timeout, then get_any_replica.

@avsej
avsej requested a balanced review from Copilot October 7, 2026 18:25

This comment was marked as resolved.

@avsej
avsej force-pushed the rcbc-558-get-replica branch from c5696aa to d268566 Compare October 7, 2026 18:29
@avsej
avsej requested a balanced review from Copilot October 7, 2026 18:29

This comment was marked as resolved.

@avsej
avsej force-pushed the rcbc-558-get-replica branch from d268566 to d8b8999 Compare October 7, 2026 23:44
@avsej
avsej requested a balanced review from Copilot October 7, 2026 23:45

This comment was marked as resolved.

@avsej
avsej force-pushed the rcbc-558-get-replica branch from d8b8999 to 4693e4f Compare October 7, 2026 23:48
@avsej
avsej requested a balanced review from Copilot October 7, 2026 23:48

This comment was marked as resolved.

@avsej
avsej force-pushed the rcbc-558-get-replica branch from 4693e4f to ca38654 Compare October 16, 2026 22:27
@avsej
avsej requested a balanced review from Copilot October 16, 2026 22:27

This comment was marked as resolved.

@avsej
avsej force-pushed the rcbc-558-get-replica branch from ca38654 to da242f8 Compare October 16, 2026 22:32
@avsej
avsej requested a balanced review from Copilot October 16, 2026 22:32

This comment was marked as resolved.

@avsej
avsej force-pushed the rcbc-558-get-replica branch from da242f8 to 3f1155b Compare October 16, 2026 22:37
@avsej
avsej requested a balanced review from Copilot October 16, 2026 22:37

This comment was marked as resolved.

@avsej
avsej force-pushed the rcbc-558-get-replica branch from 3f1155b to 37b8f91 Compare October 16, 2026 23:17
@avsej
avsej requested a balanced review from Copilot October 16, 2026 23:18

This comment was marked as resolved.

@avsej
avsej force-pushed the rcbc-558-get-replica branch from 37b8f91 to 12a0b7f Compare October 16, 2026 23:38
@avsej
avsej requested a balanced review from Copilot October 16, 2026 23:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Replica selection and wrapping depend on C++ core behavior unavailable in this snapshot and require human verification.

No open issues 🎉

Review effort: Balanced

@avsej avsej added the sdk review needed Core logic or public API changed label Oct 19, 2026
@avsej
avsej force-pushed the rcbc-558-get-replica branch from 12a0b7f to 00796db Compare October 20, 2026 19:35
@avsej
avsej requested a balanced review from Copilot October 20, 2026 19:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Native argument validation bypasses C++ cleanup on invalid inputs, leaking a cluster ownership reference.

2 open issues

Review effort: Balanced

Comment thread ext/rcb_crud.cxx Outdated
Comment thread lib/couchbase/options.rb Outdated
Motivation
----------
Collection#get_any_replica returns whichever copy of a document answers
first. A caller cannot read a named replica. RFC-0053 adds GetReplica:
a single-replica read that takes the document id and a strategy that
selects the replica.

Modifications
-------------
Collection#get_replica takes a GetReplicaStrategy.
GetReplicaStrategy.from_index accepts :first, :second or :third and a
Boolean wrap flag. Any other value raises Error::InvalidArgument before
a request is sent.

Three new errors report why a replica read fails:

- ReplicaIndexOutOfBounds: the bucket is not configured with the
  requested replica.
- ReplicaIndexCurrentlyUnavailable: the bucket is configured with it,
  but no node holds that replica right now.
- DocumentNotFoundOnReplica: the replica does not hold the document.
  It subclasses DocumentNotFound, so a rescue of the general condition
  still matches it.

The core resolves the index against the vbucket map it already holds.
The read therefore raises either replica-index error without a network
round trip. The index is checked against the configured replica count.
During a rebalance, a shorter replica chain reports
ReplicaIndexCurrentlyUnavailable.

With wrap, an index that cannot be read resolves to the next readable
replica. A bucket without replicas still raises ReplicaIndexOutOfBounds.
If no replica is readable, it still raises
ReplicaIndexCurrentlyUnavailable.

The replica is chosen from the vbucket map, which RFC-0053 specifies.
Node health is not consulted:

- A replica on a node that is down is still chosen until that node is
  failed over.
- The read can then wait for that node until the read times out.
- A read that starts after the failover raises
  ReplicaIndexCurrentlyUnavailable, or moves on with wrap.
- The from_index documentation shows how to bound the wait: a short
  timeout, then get_any_replica.

couchbase2:// raises FeatureNotAvailable. The gateway does not carry
the operation.

CI creates a second bucket with three replicas on its three KV nodes.
Its third replica has no node.

Results
-------
Integration tests read every replica the bucket places on a node. They
cover each error and both wrap cases. They do not check which node
served a read. The tests that need an unplaced replica skip when no
bucket has one.
@avsej
avsej force-pushed the rcbc-558-get-replica branch from 00796db to 020e84d Compare October 20, 2026 19:55
@avsej
avsej requested a balanced review from Copilot October 20, 2026 19:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Replica routing, wrapping, and availability semantics require validation against C++ core behavior unavailable in this checkout.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

@avsej
avsej merged commit fd3ae3f into couchbase:main Oct 21, 2026
132 of 133 checks passed
@avsej
avsej deleted the rcbc-558-get-replica branch October 21, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sdk review needed Core logic or public API changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants