Skip to content

Commit 60d4df3

Browse files
committed
chore: Transaction Options refactor
1 parent 8d39cc0 commit 60d4df3

12 files changed

Lines changed: 268 additions & 291 deletions

Spanner/src/Batch/BatchClient.php

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717

1818
namespace Google\Cloud\Spanner\Batch;
1919

20+
use Google\Cloud\Core\ArrayTrait;
2021
use Google\Cloud\Core\TimeTrait;
2122
use Google\Cloud\Spanner\Operation;
2223
use Google\Cloud\Spanner\Session\SessionCache;
2324
use Google\Cloud\Spanner\Timestamp;
2425
use Google\Cloud\Spanner\TransactionConfigurationTrait;
25-
use Google\Protobuf\Duration;
26+
use Google\Cloud\Spanner\TransactionOptionsBuilder;
2627

2728
/**
2829
* Provides Batch APIs used to read data from a Cloud Spanner database.
@@ -102,7 +103,7 @@
102103
class BatchClient
103104
{
104105
use TimeTrait;
105-
use TransactionConfigurationTrait;
106+
use ArrayTrait;
106107

107108
const PARTITION_TYPE_KEY = '__partitionTypeName';
108109
private const ALLOWED_PARTITION_TYPES = [
@@ -145,17 +146,12 @@ public function __construct(
145146
*/
146147
public function snapshot(array $options = [])
147148
{
148-
$options += [
149-
'transactionOptions' => [],
150-
];
151-
152149
// Single Use transactions are not supported in batch mode.
153150
$options['transactionOptions']['singleUse'] = false;
151+
$options['transactionOptions']['returnReadTimestamp'] = true;
154152

155-
$transactionOptions = $this->pluck('transactionOptions', $options);
156-
$transactionOptions['returnReadTimestamp'] = true;
157-
158-
$transactionOptions = $this->configureReadOnlyTransactionOptions($transactionOptions);
153+
$transactionOptions = (new TransactionOptionsBuilder())
154+
->configureReadOnlyTransactionOptions($options['transactionOptions']);
159155

160156
/** @var BatchSnapshot */
161157
return $this->operation->snapshot($this->session, [

Spanner/src/Database.php

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@
8888
class Database
8989
{
9090
use ApiHelperTrait;
91-
use TransactionConfigurationTrait;
9291
use RequestTrait;
9392

9493
public const CONTEXT_READ = 'r';
@@ -128,6 +127,15 @@ class Database
128127
private CacheItemPoolInterface $cacheItemPool;
129128
private array $info;
130129
private int $isolationLevel;
130+
private TransactionOptionsBuilder $transactionOptionsBuilder;
131+
132+
private const MUTATION_SETTERS = [
133+
'insert' => 'setInsert',
134+
'update' => 'setUpdate',
135+
'insertOrUpdate' => 'setInsertOrUpdate',
136+
'replace' => 'setReplace',
137+
'delete' => 'setDelete'
138+
];
131139

132140
/**
133141
* Create an object representing a Database.
@@ -185,6 +193,7 @@ public function __construct(
185193
);
186194

187195
$this->optionsValidator = new OptionsValidator($serializer);
196+
$this->transactionOptionsBuilder = new TransactionOptionsBuilder();
188197
$this->directedReadOptions = $instance->directedReadOptions();
189198
}
190199

@@ -747,25 +756,14 @@ public function snapshot(array $options = []): TransactionalReadInterface
747756
throw new BadMethodCallException('Nested transactions are not supported by this client.');
748757
}
749758

750-
$options += [
751-
'singleUse' => false
759+
$snapshotOptions = [
760+
'singleUse' => $options['singleUse'] ?? false
752761
];
753762

754-
$options['transactionOptions'] = $this->configureReadOnlyTransactionOptions($options);
755-
756-
// For backwards compatibility - remove all PBReadOnly fields
757-
// This was previously being done in configureReadOnlyTransactionOptions
758-
// @TODO: clean this up
759-
unset(
760-
$options['returnReadTimestamp'],
761-
$options['strong'],
762-
$options['readTimestamp'],
763-
$options['exactStaleness'],
764-
$options['minReadTimestamp'],
765-
$options['maxStaleness'],
766-
);
763+
$snapshotOptions['transactionOptions'] = $this->transactionOptionsBuilder
764+
->configureReadOnlyTransactionOptions($options);
767765

768-
return $this->operation->snapshot($this->session, $options);
766+
return $this->operation->snapshot($this->session, $snapshotOptions);
769767
}
770768

771769
/**
@@ -814,7 +812,7 @@ public function transaction(array $options = []): Transaction
814812
}
815813

816814
// Configure readWrite options here. Any nested options for readWrite should be added to this call
817-
$options['transactionOptions'] = $this->configureReadWriteTransactionOptions(
815+
$options['transactionOptions'] = $this->transactionOptionsBuilder->configureReadWriteTransactionOptions(
818816
($options['transactionOptions'] ?? []) + ['isolationLevel' => $this->isolationLevel]
819817
);
820818

@@ -921,7 +919,7 @@ public function runTransaction(callable $operation, array $options = []): mixed
921919

922920
// Configure necessary readWrite nested and base options
923921
$transactionOptions = $options['transactionOptions'] ?? [];
924-
$options['transactionOptions'] = $this->configureReadWriteTransactionOptions(
922+
$options['transactionOptions'] = $this->transactionOptionsBuilder->configureReadWriteTransactionOptions(
925923
$transactionOptions + ['isolationLevel' => $this->isolationLevel]
926924
);
927925

@@ -1666,28 +1664,27 @@ public function delete(string $table, KeySet $keySet, array $options = []): Time
16661664
*/
16671665
public function execute($sql, array $options = []): Result
16681666
{
1669-
unset($options['requestOptions']['transactionTag']);
1670-
$session = $this->pluck('session', $options, false)
1671-
?: $this->session;
1672-
1673-
list(
1674-
$options['transaction'],
1675-
$options['transactionContext']
1676-
) = $this->transactionSelector($options);
1667+
$executeOptions = $this->pluckArray(
1668+
['parameters', 'types'],
1669+
$options
1670+
);
16771671

16781672
if (isset($options['transaction']['readWrite'])) {
16791673
$options['transaction']['begin']['isolationLevel'] ??= $this->isolationLevel;
16801674
}
16811675

1682-
$options['directedReadOptions'] = $this->configureDirectedReadOptions(
1683-
$options,
1676+
[$transactionOptions, $transactionContext] = $this->transactionOptionsBuilder->transactionSelector($options);
1677+
$directedReadOptions = $this->transactionOptionsBuilder->configureDirectedReadOptions(
1678+
['transaction' => $transactionOptions] + $options,
16841679
$this->directedReadOptions
16851680
);
16861681

1687-
// Unset the internal flag.
1688-
unset($options['singleUse']);
1689-
return $this->operation->execute($session, $sql, $options + [
1690-
'route-to-leader' => $options['transactionContext'] === Database::CONTEXT_READWRITE
1682+
$session = $options['session'] ?? $this->session;
1683+
return $this->operation->execute($session, $sql, $executeOptions + [
1684+
'transaction' => $transactionOptions,
1685+
'transactionContext' => $transactionContext,
1686+
'directedReadOptions' => $directedReadOptions,
1687+
'route-to-leader' => $transactionContext === Database::CONTEXT_READWRITE
16911688
]);
16921689
}
16931690

@@ -2053,20 +2050,20 @@ public function executePartitionedUpdate($statement, array $options = []): int
20532050
*/
20542051
public function read($table, KeySet $keySet, array $columns, array $options = []): Result
20552052
{
2056-
unset($options['requestOptions']['transactionTag']);
2057-
2058-
list($transactionOptions, $context) = $this->transactionSelector($options);
2059-
$options['transaction'] = $transactionOptions;
2060-
$options['transactionContext'] = $context;
2053+
[$transactionOptions, $context] = $this->transactionOptionsBuilder->transactionSelector($options);
20612054

2062-
$options['directedReadOptions'] = $this->configureDirectedReadOptions(
2063-
$options,
2055+
$readOptions = $this->pluckArray(
2056+
['index', 'limit', 'orderBy', 'lockHint', 'directedReadOptions'],
2057+
$options
2058+
);
2059+
$readOptions['transactionContext'] = $context;
2060+
$readOptions['directedReadOptions'] = $this->transactionOptionsBuilder->configureDirectedReadOptions(
2061+
['transaction' => $transactionOptions] + $readOptions,
20642062
$this->directedReadOptions
20652063
);
2064+
$readOptions['transaction'] = $transactionOptions;
20662065

2067-
// Unset the internal flag.
2068-
unset($options['singleUse']);
2069-
return $this->operation->read($this->session, $table, $keySet, $columns, $options + [
2066+
return $this->operation->read($this->session, $table, $keySet, $columns, $readOptions + [
20702067
'route-to-leader' => $context === Database::CONTEXT_READ
20712068
]);
20722069
}

Spanner/src/SnapshotTrait.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ private function initialize(
6565

6666
$this->context = Database::CONTEXT_READ;
6767
$this->directedReadOptions = $options['directedReadOptions'] ?? [];
68-
$this->transactionSelector = array_intersect_key(
69-
(array) $options,
70-
array_flip(['singleUse', 'begin'])
68+
$this->transactionSelector = $this->pluckArray(
69+
['singleUse', 'begin'],
70+
$options,
7171
);
7272
$this->transactionOptions = $options['transactionOptions'] ?? new TransactionOptions();
73+
$this->transactionOptionsBuilder = new TransactionOptionsBuilder();
7374
}
7475

7576
/**

Spanner/src/Transaction.php

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,13 @@ public function __construct(
118118
$this->context = Database::CONTEXT_READWRITE;
119119
$this->tag = $options['tag'] ?? null;
120120
$this->isRetry = $options['isRetry'] ?? false;
121-
$this->transactionSelector = array_intersect_key(
122-
(array) $options,
123-
array_flip(['singleUse', 'begin'])
121+
$this->transactionSelector = $this->pluckArray(
122+
['singleUse', 'begin'],
123+
$options,
124124
);
125125
$this->requestOptions = $options['requestOptions'] ?? [];
126126
$this->transactionOptions = $options['transactionOptions'] ?? new TransactionOptions();
127+
$this->transactionOptionsBuilder = new TransactionOptionsBuilder();
127128

128129
if (!is_null($mapper)) {
129130
$this->mapper = $mapper;
@@ -434,18 +435,7 @@ public function commit(array $options = []): Timestamp
434435
throw new \BadMethodCallException('The transaction cannot be committed because it is not active');
435436
}
436437

437-
// set mutations, transactionId, and precommit token in the request
438-
$options['mutations'] = ($options['mutations'] ?? []) + $this->getMutations();
439-
440-
// Set the latest received precommit token from the last request from within this transaction.
441-
if ($this->precommitToken) {
442-
$options['precommitToken'] = $this->precommitToken;
443-
}
444-
// set the transaction tag if it exists
445-
unset($options['requestOptions']['requestTag']);
446-
if (isset($this->tag)) {
447-
$options['requestOptions']['transactionTag'] = $this->tag;
448-
}
438+
$mutations = ($options['mutations'] ?? []) + $this->getMutations();
449439

450440
// For commit, A transaction ID is mandatory for non-single-use transactions,
451441
// and the `begin` option is not supported (because `begin` is only used by ILBs)
@@ -455,9 +445,9 @@ public function commit(array $options = []): Timestamp
455445
'transactionOptions' => $this->transactionOptions,
456446
'singleUse' => $this->transactionSelector['singleUse'] ?? null,
457447
]);
458-
if (!empty($options['mutations'])) {
448+
if (!empty($mutations)) {
459449
// Set the mutation key if we have mutations but do not have a precommit token
460-
$mutationKey = $options['mutations'][array_rand($options['mutations'])];
450+
$mutationKey = $mutations[array_rand($mutations)];
461451
$operationTransactionOptions['mutationKey'] = $mutationKey;
462452
}
463453
// Execute the beginTransaction RPC.
@@ -470,18 +460,30 @@ public function commit(array $options = []): Timestamp
470460
$this->state = self::STATE_COMMITTED;
471461
}
472462

473-
// set transactionId in the request
474-
$options['transactionId'] = $this->transactionId;
463+
// Build the commit options
464+
$commitOptions = $this->pluckArray(
465+
['returnCommitStats', 'maxCommitDelay'],
466+
$options
467+
);
468+
// Set the latest received precommit token from the last request from within this transaction.
469+
if ($this->precommitToken) {
470+
$commitOptions['precommitToken'] = $this->precommitToken;
471+
}
472+
// set the transaction tag if it exists
473+
if ($this->tag) {
474+
$commitOptions['requestOptions']['transactionTag'] = $this->tag;
475+
}
475476

476-
$t = $this->transactionOptions($options);
477+
[$transactionOptions, $selector] = $this->transactionOptionsBuilder->transactionOptions(
478+
['transactionId' => $this->transactionId] + $options
479+
);
477480

478-
// @TODO find out what this is and clean it up
479-
$options[$t[1]] = $t[0];
481+
$commitOptions[$selector] = $transactionOptions;
480482

481483
$response = $this->operation->commit(
482484
$this->session,
483-
$this->pluck('mutations', $options, false) ?? [],
484-
$options
485+
$mutations,
486+
$commitOptions
485487
);
486488

487489
// Update commitStats
@@ -558,31 +560,30 @@ public function setPrecommitToken(MultiplexedSessionPrecommitToken $precommitTok
558560
*/
559561
private function buildUpdateOptions(array $options): array
560562
{
561-
unset($options['requestOptions']['transactionTag']);
562-
563-
if (!empty($options['transaction']['begin'])) {
564-
$options['begin'] = $this->pluck('transaction', $options)['begin'];
565-
}
566-
567-
if (isset($this->tag)) {
568-
$options['requestOptions']['transactionTag'] = $this->tag;
569-
}
570-
$options['seqno'] = $this->seqno;
571-
$this->seqno++;
572-
573563
$options['transactionType'] = $this->context;
574564
if (empty($this->transactionId) && isset($this->transactionSelector['begin'])) {
575565
$options['begin'] = $this->transactionSelector['begin'];
576566
} else {
577567
$options['transactionId'] = $this->transactionId;
578568
}
569+
[$transactionOptions] = $this->transactionOptionsBuilder->transactionSelector($options);
579570

580-
$selector = $this->transactionSelector($options);
581-
$options['transaction'] = $selector[0];
571+
$updateOptions = $this->pluckArray(['parameters', 'types'], $options);
572+
$updateOptions += [
573+
'seqno' => $this->seqno++,
574+
'transaction' => $transactionOptions,
575+
'headers' => ['spanner-route-to-leader' => ['true']]
576+
];
582577

583-
$options['headers']['spanner-route-to-leader'] = ['true'];
578+
if (isset($options['requestOptions'])) {
579+
$updateOptions['requestOptions'] = $options['requestOptions'];
580+
}
581+
582+
if ($this->tag) {
583+
$updateOptions['requestOptions']['transactionTag'] = $this->tag;
584+
}
584585

585-
return $options;
586+
return $updateOptions;
586587
}
587588

588589
public function updateFromResult(?Transaction $transaction = null): void

0 commit comments

Comments
 (0)