Skip to content

PCBC-992: Implement Zone Aware Read from Replica #201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ jobs:
php${{ matrix.php }}-tokenizer \
php${{ matrix.php }}-xml \
php${{ matrix.php }}-xmlwriter \
php${{ matrix.php }}-ctype \
openssl \
ruby \
tar \
Expand Down
26 changes: 26 additions & 0 deletions Couchbase/ClusterOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class ClusterOptions
private ?string $userAgentExtra = null;

private ?string $tlsVerifyMode = null;
private ?string $preferredServerGroup = null;
private ?string $useIpProtocol = null;

private ?ThresholdLoggingOptions $thresholdLoggingTracerOptions = null;
Expand Down Expand Up @@ -489,10 +490,33 @@ public function transactionsConfiguration(TransactionsConfiguration $options): C
return $this;
}

/**
* Select the server group to use for replica APIs.
*
* For some use-cases it might be necessary to restrict list of the nodes,
* that are used in replica read APIs to single server group to optimize
* network costs.
*
* @see https://docs.couchbase.com/server/current/manage/manage-groups/manage-groups.html
*
* @param string $serverGroupName
*
* @return ClusterOptions
* @since 4.2.6
*/
public function preferredServerGroup(string $serverGroupName): ClusterOptions
{
$this->preferredServerGroup = $serverGroupName;
return $this;
}

/**
* Applies configuration profile to ClusterOptions associating string to range of options
*
* @param string $profile name of config profile to apply (e.g. wan_development)
*
* @throws InvalidArgumentException
*
* @since 4.0.1
*/
public function applyProfile(string $profile): void
Expand Down Expand Up @@ -580,6 +604,8 @@ public function export(): array

'tlsVerify' => $this->tlsVerifyMode,

'preferredServerGroup' => $this->preferredServerGroup,

'thresholdLoggingTracerOptions' =>
$this->thresholdLoggingTracerOptions == null ? null : $this->thresholdLoggingTracerOptions->export(),
'loggingMeterOptions' => $this->loggingMeterOptions == null ? null : $this->loggingMeterOptions->export(),
Expand Down
21 changes: 21 additions & 0 deletions Couchbase/GetAllReplicasOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
class GetAllReplicasOptions
{
private Transcoder $transcoder;
private string $readPreference;
private ?int $timeoutMilliseconds = null;

/**
Expand All @@ -31,6 +32,7 @@ class GetAllReplicasOptions
public function __construct()
{
$this->transcoder = JsonTranscoder::getInstance();
$this->readPreference = ReadPreference::NO_PREFERENCE;
}

/**
Expand Down Expand Up @@ -72,6 +74,24 @@ public function transcoder(Transcoder $transcoder): GetAllReplicasOptions
return $this;
}

/**
* Choose how the replica nodes will be selected. By default, it has no
* preference and will select any available replica, but it is possible to
* prioritize or restrict to only nodes in local server group
*
* @see ReadPreference
*
* @param string $readPreference
*
* @return GetAllReplicasOptions
* @since 4.2.6
*/
public function readPreference(string $readPreference): GetAllReplicasOptions
{
$this->readPreference = $readPreference;
return $this;
}

/**
* Returns associated transcoder.
*
Expand Down Expand Up @@ -103,6 +123,7 @@ public static function export(?GetAllReplicasOptions $options): array
}
return [
'timeoutMilliseconds' => $options->timeoutMilliseconds,
'readPreference' => $options->readPreference,
];
}
}
21 changes: 21 additions & 0 deletions Couchbase/GetAnyReplicaOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
class GetAnyReplicaOptions
{
private Transcoder $transcoder;
private string $readPreference;
private ?int $timeoutMilliseconds = null;

/**
Expand All @@ -31,6 +32,7 @@ class GetAnyReplicaOptions
public function __construct()
{
$this->transcoder = JsonTranscoder::getInstance();
$this->readPreference = ReadPreference::NO_PREFERENCE;
}

/**
Expand Down Expand Up @@ -72,6 +74,24 @@ public function transcoder(Transcoder $transcoder): GetAnyReplicaOptions
return $this;
}

/**
* Choose how the replica nodes will be selected. By default, it has no
* preference and will select any available replica, but it is possible to
* prioritize or restrict to only nodes in local server group
*
* @see ReadPreference
*
* @param string $readPreference
*
* @return GetAnyReplicaOptions
* @since 4.2.6
*/
public function readPreference(string $readPreference): GetAnyReplicaOptions
{
$this->readPreference = $readPreference;
return $this;
}

/**
* Returns associated transcoder.
*
Expand Down Expand Up @@ -103,6 +123,7 @@ public static function export(?GetAnyReplicaOptions $options): array
}
return [
'timeoutMilliseconds' => $options->timeoutMilliseconds,
'readPreference' => $options->readPreference,
];
}
}
21 changes: 21 additions & 0 deletions Couchbase/LookupInAllReplicasOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
class LookupInAllReplicasOptions
{
private Transcoder $transcoder;
private string $readPreference;
private ?int $timeoutMilliseconds = null;
private ?bool $withExpiry = null;

Expand All @@ -32,6 +33,7 @@ class LookupInAllReplicasOptions
public function __construct()
{
$this->transcoder = JsonTranscoder::getInstance();
$this->readPreference = ReadPreference::NO_PREFERENCE;
}

/**
Expand Down Expand Up @@ -59,6 +61,24 @@ public function timeout(int $milliseconds): LookupInAllReplicasOptions
return $this;
}

/**
* Choose how the replica nodes will be selected. By default, it has no
* preference and will select any available replica, but it is possible to
* prioritize or restrict to only nodes in local server group
*
* @see ReadPreference
*
* @param string $readPreference
*
* @return LookupInAllReplicasOptions
* @since 4.2.6
*/
public function readPreference(string $readPreference): LookupInAllReplicasOptions
{
$this->readPreference = $readPreference;
return $this;
}

/**
* Sets whether to include document expiry with the document content.
*
Expand Down Expand Up @@ -136,6 +156,7 @@ public static function export(?LookupInAllReplicasOptions $options): array
}
return [
'timeoutMilliseconds' => $options->timeoutMilliseconds,
'readPreference' => $options->readPreference,
];
}
}
21 changes: 21 additions & 0 deletions Couchbase/LookupInAnyReplicaOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
class LookupInAnyReplicaOptions
{
private Transcoder $transcoder;
private string $readPreference;
private ?int $timeoutMilliseconds = null;
private ?bool $withExpiry = null;

Expand All @@ -32,6 +33,7 @@ class LookupInAnyReplicaOptions
public function __construct()
{
$this->transcoder = JsonTranscoder::getInstance();
$this->readPreference = ReadPreference::NO_PREFERENCE;
}

/**
Expand Down Expand Up @@ -59,6 +61,24 @@ public function timeout(int $milliseconds): LookupInAnyReplicaOptions
return $this;
}

/**
* Choose how the replica nodes will be selected. By default, it has no
* preference and will select any available replica, but it is possible to
* prioritize or restrict to only nodes in local server group
*
* @see ReadPreference
*
* @param string $readPreference
*
* @return LookupInAnyReplicaOptions
* @since 4.2.6
*/
public function readPreference(string $readPreference): LookupInAnyReplicaOptions
{
$this->readPreference = $readPreference;
return $this;
}

/**
* Sets whether to include document expiry with the document content.
*
Expand Down Expand Up @@ -135,6 +155,7 @@ public static function export(?LookupInAnyReplicaOptions $options): array
}
return [
'timeoutMilliseconds' => $options->timeoutMilliseconds,
'readPreference' => $options->readPreference,
];
}
}
46 changes: 46 additions & 0 deletions Couchbase/ReadPreference.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

/**
* Copyright 2014-Present Couchbase, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace Couchbase;

/**
* Select read preference (or affinity) for the replica APIs such as:
*
* @see Collection::getAllReplicas()
* @see Collection::getAnyReplica()
* @see Collection::lookupInAllReplicas()
* @see Collection::lookupInAnyReplica()
* @see TransactionAttemptContext::getReplicaFromPreferredServerGroup()
*
* @see https://docs.couchbase.com/server/current/manage/manage-groups/manage-groups.html
*/
interface ReadPreference
{
/**
* Do not enforce any filtering for replica set.
*/
public const NO_PREFERENCE = "noPreference";

/**
* Exclude any nodes that do not belong to local group selected during
* cluster instantiation with @see ClusterOptions::preferredServerGroup()
*/
public const SELECTED_SERVER_GROUP = "selectedServerGroup";
}
14 changes: 14 additions & 0 deletions Couchbase/TransactionAttemptContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ public function get(Collection $collection, string $id): TransactionGetResult
return new TransactionGetResult($response, GetOptions::getTranscoder(null));
}

public function getReplicaFromPreferredServerGroup(Collection $collection, string $id, TransactionGetReplicaOptions $options = null): TransactionGetResult
{
$function = COUCHBASE_EXTENSION_NAMESPACE . '\\transactionGetReplicaFromPreferredServerGroup';
$response = $function(
$this->transaction,
$collection->bucketName(),
$collection->scopeName(),
$collection->name(),
$id
);

return new TransactionGetResult($response, TransactionGetReplicaOptions::getTranscoder($options));
}

/**
* Inserts a new document to the collection, failing if the document already exists.
*
Expand Down
75 changes: 75 additions & 0 deletions Couchbase/TransactionGetReplicaOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/**
* Copyright 2014-Present Couchbase, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace Couchbase;

class TransactionGetReplicaOptions
{
private Transcoder $transcoder;

/**
* @since 4.2.6
*/
public function __construct()
{
$this->transcoder = JsonTranscoder::getInstance();
}

/**
* Static helper to keep code more readable
*
* @return TransactionGetReplicaOptions
* @since 4.2.6
*/
public static function build(): TransactionGetReplicaOptions
{
return new TransactionGetReplicaOptions();
}

/**
* Associate custom transcoder with the request.
*
* @param Transcoder $transcoder
*
* @return TransactionGetReplicaOptions
* @since 4.2.6
*/
public function transcoder(Transcoder $transcoder): TransactionGetReplicaOptions
{
$this->transcoder = $transcoder;
return $this;
}

/**
* Returns associated transcoder.
*
* @param TransactionGetReplicaOptions|null $options
*
* @return Transcoder
* @since 4.2.6
*/
public static function getTranscoder(?TransactionGetReplicaOptions $options): Transcoder
{
if ($options == null) {
return JsonTranscoder::getInstance();
}
return $options->transcoder;
}
}
Loading
Loading