diff --git a/Spanner/src/Backup.php b/Spanner/src/Backup.php index 27dbd8261ff..389f87480d9 100644 --- a/Spanner/src/Backup.php +++ b/Spanner/src/Backup.php @@ -124,9 +124,9 @@ public function create( } $request = $this->serializer->decodeMessage(new CreateBackupRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->instance->name()); - - return $this->databaseAdminClient->createBackup($request, $callOptions) + return $this->databaseAdminClient->createBackup($request, $callOptions + [ + 'resource-prefix' => $this->instance->name(), + ]) ->withResultFunction($this->backupResultFunction()); } @@ -168,9 +168,10 @@ public function createCopy( ]; $request = $this->serializer->decodeMessage(new CopyBackupRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->instance->name()); - return $this->databaseAdminClient->copyBackup($request, $callOptions) + return $this->databaseAdminClient->copyBackup($request, $callOptions + [ + 'resource-prefix' => $this->instance->name(), + ]) ->withResultFunction($this->backupResultFunction()); } @@ -193,9 +194,10 @@ public function delete(array $options = []): void ]; $request = $this->serializer->decodeMessage(new DeleteBackupRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $this->databaseAdminClient->deleteBackup($request, $callOptions); + $this->databaseAdminClient->deleteBackup($request, $callOptions + [ + 'resource-prefix' => $this->name, + ]); } /** @@ -277,9 +279,10 @@ public function reload(array $options = []): array ]; $request = $this->serializer->decodeMessage(new GetBackupRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $response = $this->databaseAdminClient->getBackup($request, $callOptions); + $response = $this->databaseAdminClient->getBackup($request, $callOptions + [ + 'resource-prefix' => $this->name, + ]); return $this->info = $this->handleResponse($response); } @@ -340,9 +343,10 @@ public function updateExpireTime(DateTimeInterface $newTimestamp, array $options ]; $request = $this->serializer->decodeMessage(new UpdateBackupRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $response = $this->databaseAdminClient->updateBackup($request, $callOptions); + $response = $this->databaseAdminClient->updateBackup($request, $callOptions + [ + 'resource-prefix' => $this->name, + ]); return $this->info = $this->handleResponse($response); } diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index 8e3c89678a1..60f11122465 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -358,9 +358,10 @@ public function reload(array $options = []): array $data['name'] = $this->name; $request = $this->serializer->decodeMessage(new GetDatabaseRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $response = $this->databaseAdminClient->getDatabase($request, $callOptions); + $response = $this->databaseAdminClient->getDatabase($request, $callOptions + [ + 'resource-prefix' => $this->name, + ]); return $this->info = $this->handleResponse($response); } @@ -423,9 +424,9 @@ public function create(array $options = []): OperationResponse ]; $request = $this->serializer->decodeMessage(new CreateDatabaseRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->instance->name()); - - return $this->databaseAdminClient->createDatabase($request, $callOptions) + return $this->databaseAdminClient->createDatabase($request, $callOptions + [ + 'resource-prefix' => $this->instance->name(), + ]) ->withResultFunction($this->databaseResultFunction()); } @@ -488,9 +489,10 @@ public function updateDatabase(array $options = []): OperationResponse ]; $request = $this->serializer->decodeMessage(new UpdateDatabaseRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - return $this->databaseAdminClient->updateDatabase($request, $callOptions) + return $this->databaseAdminClient->updateDatabase($request, $callOptions + [ + 'resource-prefix' => $this->name, + ]) ->withResultFunction($this->databaseResultFunction()); } @@ -563,9 +565,9 @@ public function updateDdlBatch(array $statements, array $options = []): Operatio ]; $request = $this->serializer->decodeMessage(new UpdateDatabaseDdlRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - - return $this->databaseAdminClient->updateDatabaseDdl($request, $callOptions); + return $this->databaseAdminClient->updateDatabaseDdl($request, $callOptions + [ + 'resource-prefix' => $this->name + ]); } /** @@ -596,9 +598,10 @@ public function drop(array $options = []): void $data['database'] = $this->name; $request = $this->serializer->decodeMessage(new DropDatabaseRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $this->databaseAdminClient->dropDatabase($request, $callOptions); + $this->databaseAdminClient->dropDatabase($request, $callOptions + [ + 'resource-prefix' => $this->name + ]); if ($this->sessionPool) { $this->sessionPool->clear(); @@ -633,9 +636,10 @@ public function ddl(array $options = []): array $data['database'] = $this->name; $request = $this->serializer->decodeMessage(new GetDatabaseDdlRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $response = $this->databaseAdminClient->getDatabaseDdl($request, $callOptions); + $response = $this->databaseAdminClient->getDatabaseDdl($request, $callOptions + [ + 'resource-prefix' => $this->name + ]); $ddl = $this->handleResponse($response); if (isset($ddl['statements'])) { @@ -1683,7 +1687,6 @@ public function execute($sql, array $options = []): Result $options['transaction'], $options['transactionContext'] ) = $this->transactionSelector($options); - $options = $this->addLarHeader($options, true, $options['transactionContext']); $options['directedReadOptions'] = $this->configureDirectedReadOptions( $options, @@ -1693,7 +1696,9 @@ public function execute($sql, array $options = []): Result try { // Unset the internal flag. unset($options['singleUse']); - return $this->operation->execute($session, $sql, $options); + return $this->operation->execute($session, $sql, $options + [ + 'route-to-leader' => $options['transactionContext'] === SessionPoolInterface::CONTEXT_READWRITE + ]); } finally { $session->setExpiration(); } @@ -1782,10 +1787,11 @@ public function batchWrite(array $mutationGroups, array $options = []): \Generat ]; $request = $this->serializer->decodeMessage(new BatchWriteRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - $response = $this->spannerClient->batchWrite($request, $callOptions); + $response = $this->spannerClient->batchWrite($request, $callOptions + [ + 'resource-prefix' => $this->name, + 'route-to-leader' => $this->routeToLeader, + ]); return $this->handleResponse($response); } finally { $this->isRunningTransaction = false; @@ -1927,11 +1933,10 @@ public function executePartitionedUpdate($statement, array $options = []): int } $transaction = $this->operation->transaction($session, $beginTransactionOptions); - $options = $this->addLarHeader($options); - try { return $this->operation->executeUpdate($session, $transaction, $statement, [ - 'statsItem' => 'rowCountLowerBound' + 'statsItem' => 'rowCountLowerBound', + 'route-to-leader' => true, ] + $options); } finally { $session->setExpiration(); @@ -2072,12 +2077,12 @@ public function read($table, KeySet $keySet, array $columns, array $options = [] $this->directedReadOptions ?? [] ); - $options = $this->addLarHeader($options, true, $context); - try { // Unset the internal flag. unset($options['singleUse']); - return $this->operation->read($session, $table, $keySet, $columns, $options); + return $this->operation->read($session, $table, $keySet, $columns, $options + [ + 'route-to-leader' => $context === SessionPoolInterface::CONTEXT_READ + ]); } finally { $session->setExpiration(); } @@ -2203,10 +2208,10 @@ public function batchCreateSessions(array $options): array $data['database'] = $this->name; $request = $this->serializer->decodeMessage(new BatchCreateSessionsRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - - $response = $this->spannerClient->batchCreateSessions($request, $callOptions); + $response = $this->spannerClient->batchCreateSessions($request, $callOptions + [ + 'resource-prefix' => $this->name, + 'route-to-leader' => $this->routeToLeader + ]); return $this->handleResponse($response); } @@ -2225,9 +2230,9 @@ public function deleteSessionAsync(array $options): PromiseInterface [$data, $callOptions] = $this->splitOptionalArgs($options); $request = $this->serializer->decodeMessage(new DeleteSessionRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - - return $this->spannerClient->deleteSessionAsync($request, $callOptions); + return $this->spannerClient->deleteSessionAsync($request, $callOptions + [ + 'resource-prefix' => $this->name + ]); } /** @@ -2254,14 +2259,13 @@ public function deleteSessionAsync(array $options): PromiseInterface public function backupOperations(array $options = []): ItemIterator { [$data, $callOptions] = $this->splitOptionalArgs($options); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); $request = $this->serializer->decodeMessage(new ListBackupOperationsRequest(), $data); $request->setParent($this->instance->name()); return $this->buildLongRunningIterator( [$this->databaseAdminClient, 'listBackupOperations'], $request, - $callOptions + $callOptions + ['resource-prefix' => $this->name] ); } @@ -2286,9 +2290,9 @@ public function createDatabaseFromBackup($name, $backup, array $options = []): O ]; $request = $this->serializer->decodeMessage(new RestoreDatabaseRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - - return $this->databaseAdminClient->restoreDatabase($request, $callOptions) + return $this->databaseAdminClient->restoreDatabase($request, $callOptions + [ + 'resource-prefix' => $this->name + ]) ->withResultFunction($this->databaseResultFunction()); } @@ -2316,14 +2320,13 @@ public function createDatabaseFromBackup($name, $backup, array $options = []): O public function databaseOperations(array $options = []): ItemIterator { [$data, $callOptions] = $this->splitOptionalArgs($options); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); $request = $this->serializer->decodeMessage(new ListDatabaseOperationsRequest(), $data); $request->setParent($this->instance->name()); return $this->buildLongRunningIterator( [$this->databaseAdminClient, 'listDatabaseOperations'], $request, - $callOptions + $callOptions + ['resource-prefix' => $this->name] ); } diff --git a/Spanner/src/Instance.php b/Spanner/src/Instance.php index 034b41fee09..e9365607e1d 100644 --- a/Spanner/src/Instance.php +++ b/Spanner/src/Instance.php @@ -198,9 +198,10 @@ public function exists(array $options = []): bool 'fieldMask' => ['paths' => ['name']], ]; $request = $this->serializer->decodeMessage(new GetInstanceRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); - $this->instanceAdminClient->getInstance($request, $callOptions); + $this->instanceAdminClient->getInstance($request, $callOptions + [ + 'resource-prefix' => $this->projectName + ]); } else { $this->reload($options); } @@ -252,9 +253,10 @@ public function reload(array $options = []): array } $request = $this->serializer->decodeMessage(new GetInstanceRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); - $response = $this->instanceAdminClient->getInstance($request, $callOptions); + $response = $this->instanceAdminClient->getInstance($request, $callOptions + [ + 'resource-prefix' => $this->projectName + ]); return $this->info = $this->handleResponse($response); } @@ -303,9 +305,10 @@ public function create(InstanceConfiguration $config, array $options = []): Oper ]; $request = $this->serializer->decodeMessage(new CreateInstanceRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - return $this->instanceAdminClient->createInstance($request, $callOptions) + return $this->instanceAdminClient->createInstance($request, $callOptions + [ + 'resource-prefix' => $this->name + ]) ->withResultFunction($this->instanceResultFunction()); } @@ -382,9 +385,10 @@ public function update(array $options = []): OperationResponse ]; $request = $this->serializer->decodeMessage(new UpdateInstanceRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - return $this->instanceAdminClient->updateInstance($request, $callOptions) + return $this->instanceAdminClient->updateInstance($request, $callOptions + [ + 'resource-prefix' => $this->name + ]) ->withResultFunction($this->instanceResultFunction()); } @@ -409,9 +413,10 @@ public function delete(array $options = []): void $data['name'] = $this->name; $request = $this->serializer->decodeMessage(new DeleteInstanceRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); - $this->instanceAdminClient->deleteInstance($request, $callOptions); + $this->instanceAdminClient->deleteInstance($request, $callOptions + [ + 'resource-prefix' => $this->name + ]); } /** @@ -539,12 +544,11 @@ public function databases(array $options = []): ItemIterator $data['parent'] = $this->name; $request = $this->serializer->decodeMessage(new ListDatabasesRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); return $this->buildListItemsIterator( [$this->databaseAdminClient, 'listDatabases'], $request, - $callOptions, + $callOptions + ['resource-prefix' => $this->name], function (array $database) { return $this->database($database['name'], ['database' => $database]); }, @@ -614,12 +618,11 @@ public function backups(array $options = []): ItemIterator $data['parent'] = $this->name; $request = $this->serializer->decodeMessage(new ListBackupsRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); return $this->buildListItemsIterator( [$this->databaseAdminClient, 'listBackups'], $request, - $callOptions, + $callOptions + ['resource-prefix' => $this->name], function (array $backup) { return $this->backup($backup['name'], $backup); }, diff --git a/Spanner/src/InstanceConfiguration.php b/Spanner/src/InstanceConfiguration.php index 9f5ba1774d7..c58ecc61da4 100644 --- a/Spanner/src/InstanceConfiguration.php +++ b/Spanner/src/InstanceConfiguration.php @@ -170,15 +170,12 @@ public function reload(array $options = []) { [$data, $callOptions] = $this->splitOptionalArgs($options); $data += ['name' => $this->name]; - $callOptions = $this->addResourcePrefixHeader( - $callOptions, - InstanceAdminClient::projectName($this->projectId) - ); - $response = $this->instanceAdminClient->getInstanceConfig( - $this->serializer->decodeMessage(new GetInstanceConfigRequest(), $data), - $callOptions - ); + $request = $this->serializer->decodeMessage(new GetInstanceConfigRequest(), $data); + + $response = $this->instanceAdminClient->getInstanceConfig($request, $callOptions + [ + 'resource-prefix' => InstanceAdminClient::projectName($this->projectId), + ]); return $this->info = $this->handleResponse($response); } @@ -241,11 +238,10 @@ public function create(InstanceConfiguration $baseConfig, array $replicas, array new CreateInstanceConfigRequest(), $requestArray ); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); $operationResponse = $this->instanceAdminClient->createInstanceConfig( $request, - $callOptions + $callOptions + ['resource-prefix' => $this->name] ); return $operationResponse @@ -291,11 +287,10 @@ public function update(array $options = []) 'updateMask' => $this->fieldMask($data), 'validateOnly' => $validateOnly ]); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->name); $operationResponse = $this->instanceAdminClient->updateInstanceConfig( $request, - $callOptions + $callOptions + ['resource-prefix' => $this->name] ); return $operationResponse @@ -323,10 +318,11 @@ public function delete(array $options = []) [$data, $callOptions] = $this->splitOptionalArgs($options); $data += ['name' => $this->name]; - $this->instanceAdminClient->deleteInstanceConfig( - $this->serializer->decodeMessage(new DeleteInstanceConfigRequest(), $data), - $this->addResourcePrefixHeader($callOptions, $this->name) - ); + $request = $this->serializer->decodeMessage(new DeleteInstanceConfigRequest(), $data); + + $this->instanceAdminClient->deleteInstanceConfig($request, $callOptions + [ + 'resource-prefix' => $this->name + ]); } /** diff --git a/Core/src/Middleware/ExceptionMiddleware.php b/Spanner/src/Middleware/SpannerMiddleware.php similarity index 74% rename from Core/src/Middleware/ExceptionMiddleware.php rename to Spanner/src/Middleware/SpannerMiddleware.php index 212c76acc2c..1bfca02c829 100644 --- a/Core/src/Middleware/ExceptionMiddleware.php +++ b/Spanner/src/Middleware/SpannerMiddleware.php @@ -30,32 +30,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -namespace Google\Cloud\Core\Middleware; +namespace Google\Cloud\Spanner\Middleware; +use Google\ApiCore\ArrayTrait; use Google\ApiCore\ApiException; use Google\ApiCore\BidiStream; use Google\ApiCore\Call; use Google\ApiCore\ClientStream; use Google\ApiCore\Middleware\MiddlewareInterface; -use Google\ApiCore\Serializer; +use Google\Cloud\Spanner\Serializer; use Google\ApiCore\ServerStream; use Google\Cloud\Core\RequestProcessorTrait; use GuzzleHttp\Promise\PromiseInterface; use Throwable; /** - * Middleware that wraps any Api Exception to a `Google\Cloud\Core\Exception` - * exception class. This is primarily to maintain backwards compatibility with - * previous Spanner versions. + * Middleware for Spanner that adds the following functionality: + * + * - Wraps any Api Exception to a `Google\Cloud\Core\Exception` exception + * class. This is primarily to maintain backwards compatibility with previous + * Spanner versions. + * - * * @internal */ -class ExceptionMiddleware implements MiddlewareInterface +class SpannerMiddleware implements MiddlewareInterface { + use ArrayTrait; + + private const ROUTE_TO_LEADER_HEADER = 'x-goog-spanner-route-to-leader'; + private const RESOURCE_PREFIX_HEADER = 'google-cloud-resource-prefix'; + use RequestProcessorTrait; /** @var callable */ private $nextHandler; + private $serializer; public function __construct(callable $nextHandler) { @@ -71,6 +81,14 @@ public function __construct(callable $nextHandler) */ public function __invoke(Call $call, array $options) { + if ($resourcePrefix = $this->pluck('resource-prefix', $options, false)) { + $options['headers'][self::RESOURCE_PREFIX_HEADER] = [$options['resource-prefix']]; + } + + if (true === $this->pluck('route-to-leader', $options, false)) { + $options['headers'][self::ROUTE_TO_LEADER_HEADER] = ['true']; + } + $response = ($this->nextHandler)($call, $options); if ($response instanceof PromiseInterface) { return $response->then(null, function ($value) { @@ -82,6 +100,7 @@ public function __invoke(Call $call, array $options) } }); } + // this can also be a Stream return $response; } diff --git a/Spanner/src/Operation.php b/Spanner/src/Operation.php index 22451bd377c..283f5629b29 100644 --- a/Spanner/src/Operation.php +++ b/Spanner/src/Operation.php @@ -17,6 +17,8 @@ namespace Google\Cloud\Spanner; +use Google\Cloud\Core\ApiHelperTrait; +use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; use Google\Cloud\Spanner\Session\Session; @@ -47,7 +49,8 @@ */ class Operation { - use RequestTrait; + use ApiHelperTrait; + use RequestProcessorTrait; use MutationTrait; const OP_INSERT = 'insert'; @@ -163,10 +166,10 @@ public function commitWithResponse(Session $session, array $mutations, array $op $data = $this->formatSingleUseTransactionOptions($data); $request = $this->serializer->decodeMessage(new CommitRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - - $response = $this->spannerClient->commit($request, $callOptions); + $response = $this->spannerClient->commit($request, $callOptions + [ + 'resource-prefix' => $this->getDatabaseNameFromSession($session), + 'route-to-leader' => $this->routeToLeader + ]); $timestamp = $response->getCommitTimestamp(); return [ @@ -202,10 +205,10 @@ public function rollback(Session $session, $transactionId, array $options = []): ]; $request = $this->serializer->decodeMessage(new RollbackRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - - $this->spannerClient->rollback($request, $callOptions); + $this->spannerClient->rollback($request, $callOptions + [ + 'resource-prefix' => $this->getDatabaseNameFromSession($session), + 'route-to-leader' => $this->routeToLeader + ]); } /** @@ -377,10 +380,10 @@ public function executeUpdateBatch( ]; $request = $this->serializer->decodeMessage(new ExecuteBatchDmlRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - - $response = $this->spannerClient->executeBatchDml($request, $callOptions); + $response = $this->spannerClient->executeBatchDml($request, $callOptions + [ + 'resource-prefix' => $this->getDatabaseNameFromSession($session), + 'route-to-leader' => $this->routeToLeader + ]); $res = $this->handleResponse($response); if (empty($transaction->id())) { @@ -663,10 +666,10 @@ public function createSession($databaseName, array $options = []): Session $request = $this->serializer->decodeMessage(new CreateSessionRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $databaseName); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - - $response = $this->spannerClient->createSession($request, $callOptions); + $response = $this->spannerClient->createSession($request, $callOptions + [ + 'resource-prefix' => $databaseName, + 'route-to-leader' => $this->routeToLeader + ]); $res = $this->handleResponse($response); return $this->session($res['name']); @@ -753,10 +756,11 @@ public function partitionQuery( ]; $request = $this->serializer->decodeMessage(new PartitionQueryRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - $response = $this->spannerClient->partitionQuery($request, $callOptions); + $response = $this->spannerClient->partitionQuery($request, $callOptions + [ + 'resource-prefix' => $this->getDatabaseNameFromSession($session), + 'route-to-leader' => $this->routeToLeader + ]); $res = $this->handleResponse($response); $partitions = []; @@ -816,10 +820,11 @@ public function partitionRead( ]; $request = $this->serializer->decodeMessage(new PartitionReadRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - $response = $this->spannerClient->partitionRead($request, $callOptions); + $response = $this->spannerClient->partitionRead($request, $callOptions + [ + 'resource-prefix' => $this->getDatabaseNameFromSession($session), + 'route-to-leader' => $this->routeToLeader + ]); $res = $this->handleResponse($response); $partitions = []; @@ -866,19 +871,21 @@ private function beginTransaction(Session $session, array $options = []): array $transactionOptions = $this->formatTransactionOptions( $this->pluck('transactionOptions', $data, false) ?: [] ); - if (isset($transactionOptions['readWrite']) - || isset($transactionOptions['partitionedDml'])) { - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - } + $routeToLeader = ( + isset($transactionOptions['readWrite']) || isset($transactionOptions['partitionedDml']) + ) && $this->routeToLeader; + $data += [ 'session' => $session->name(), 'options' => $transactionOptions ]; $request = $this->serializer->decodeMessage(new BeginTransactionRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); - $response = $this->spannerClient->beginTransaction($request, $callOptions); + $response = $this->spannerClient->beginTransaction($request, $callOptions + [ + 'resource-prefix' => $this->getDatabaseNameFromSession($session), + 'route-to-leader' => $routeToLeader, + ]); return $this->handleResponse($response); } @@ -1066,17 +1073,21 @@ private function formatTransactionOptions(array $transactionOptions): array */ private function executeStreamingSql(array $args) { - list($data, $callOptions) = $this->splitOptionalArgs($args); + list($data, $callOptions) = $this->splitOptionalArgs($args, ['route-to-leader']); $data = $this->formatSqlParams($data); $data['transaction'] = $this->createTransactionSelector($data); $data['queryOptions'] = $this->createQueryOptions($data); - $callOptions = $this->conditionallyUnsetLarHeader($callOptions, $this->routeToLeader); + if (!$this->routeToLeader) { + unset($callOptions['route-to-leader']); + } + $databaseName = $this->pluck('database', $data); $request = $this->serializer->decodeMessage(new ExecuteSqlRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $databaseName); - $response = $this->spannerClient->executeStreamingSql($request, $callOptions); + $response = $this->spannerClient->executeStreamingSql($request, $callOptions + [ + 'resource-prefix' => $databaseName, + ]); return $this->handleResponse($response); } @@ -1086,15 +1097,18 @@ private function executeStreamingSql(array $args) */ private function streamingRead(array $args): \Generator { - list($data, $callOptions) = $this->splitOptionalArgs($args); + list($data, $callOptions) = $this->splitOptionalArgs($args, ['route-to-leader']); $data['transaction'] = $this->createTransactionSelector($data); - $callOptions = $this->conditionallyUnsetLarHeader($callOptions, $this->routeToLeader); + if (!$this->routeToLeader) { + unset($callOptions['route-to-leader']); + } $databaseName = $this->pluck('database', $data); $request = $this->serializer->decodeMessage(new ReadRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $databaseName); - $response = $this->spannerClient->streamingRead($request, $callOptions); + $response = $this->spannerClient->streamingRead($request, $callOptions + [ + 'resource-prefix' => $databaseName, + ]); return $this->handleResponse($response); } @@ -1132,23 +1146,6 @@ private function formatPartitionQueryOptions(array $args): array return $args; } - /** - * Conditionally unset the LAR header. - * - * @param array $args Request arguments. - * @param bool $value Whether to set or unset the LAR header. - * @return array - */ - private function conditionallyUnsetLarHeader( - array $args, - bool $value = true - ): array { - if (!$value) { - unset($args['headers'][$this->larHeader]); - } - return $args; - } - /** * Represent the class in a more readable and digestable fashion. * diff --git a/Spanner/src/RequestTrait.php b/Spanner/src/RequestTrait.php index e335cf09466..10d9d3e1dcb 100644 --- a/Spanner/src/RequestTrait.php +++ b/Spanner/src/RequestTrait.php @@ -36,45 +36,6 @@ trait RequestTrait use ApiHelperTrait; use RequestProcessorTrait; - private $larHeader = 'x-goog-spanner-route-to-leader'; - private $resourcePrefixHeader = 'google-cloud-resource-prefix'; - - /** - * Add the `x-goog-spanner-route-to-leader` header value to the request. - * - * @param array $args Request arguments. - * @param bool $value LAR header value. - * @param string $context Transaction context. - * @return array - */ - private function addLarHeader( - array $args, - bool $value = true, - string $context = SessionPoolInterface::CONTEXT_READWRITE - ) { - if (!$value) { - return $args; - } - // If value is true and context is READWRITE, set LAR header. - if ($context === SessionPoolInterface::CONTEXT_READWRITE) { - $args['headers'][$this->larHeader] = ['true']; - } - return $args; - } - - /** - * Add the `google-cloud-resource-prefix` header value to the request. - * - * @param array $args Request arguments. - * @param string $value Resource prefix header value. - * @return array - */ - private function addResourcePrefixHeader(array $args, string $value) - { - $args['headers'][$this->resourcePrefixHeader] = [$value]; - return $args; - } - /** * Helper making list calls for long running operations. * diff --git a/Spanner/src/Session/Session.php b/Spanner/src/Session/Session.php index f293d8e12c5..a081d4b0c76 100644 --- a/Spanner/src/Session/Session.php +++ b/Spanner/src/Session/Session.php @@ -17,9 +17,9 @@ namespace Google\Cloud\Spanner\Session; +use Google\Cloud\Core\ApiHelperTrait; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Spanner\Database; -use Google\Cloud\Spanner\RequestTrait; use Google\Cloud\Spanner\Serializer; use Google\Cloud\Spanner\V1\Client\SpannerClient; use Google\Cloud\Spanner\V1\DeleteSessionRequest; @@ -30,7 +30,7 @@ */ class Session { - use RequestTrait; + use ApiHelperTrait; /** * @var int|null @@ -117,10 +117,11 @@ public function exists(array $options = []) try { $request = $this->serializer->decodeMessage(new GetSessionRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->databaseName); - $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); - $this->spannerClient->getSession($request, $callOptions); + $this->spannerClient->getSession($request, $callOptions + [ + 'resource-prefix' => $this->databaseName, + 'route-to-leader' => $this->routeToLeader, + ]); } catch (NotFoundException $e) { return false; } @@ -141,9 +142,10 @@ public function delete(array $options = []) ]; $request = $this->serializer->decodeMessage(new DeleteSessionRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->databaseName); - $this->spannerClient->deleteSession($request, $callOptions); + $this->spannerClient->deleteSession($request, $callOptions + [ + 'resource-prefix' => $this->databaseName, + ]); } /** diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index 8b3f9655cdb..600d1b82a55 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -34,6 +34,7 @@ use Google\Cloud\Spanner\Admin\Instance\V1\ListInstanceConfigsRequest; use Google\Cloud\Spanner\Admin\Instance\V1\ListInstancesRequest; use Google\Cloud\Spanner\Admin\Instance\V1\ReplicaInfo; +use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; use Google\Cloud\Spanner\Batch\BatchClient; use Google\Cloud\Spanner\Middleware\SpannerMiddleware; use Google\Cloud\Spanner\Session\SessionPoolInterface; @@ -255,17 +256,21 @@ public function __construct(array $config = []) 'libName' => 'gccl', 'serializer' => $this->serializer, ]; - $middleware = function (MiddlewareInterface $handler) { - return new SpannerMiddleware($handler); - }; $this->spannerClient = $config['gapicSpannerClient'] ?? new GapicSpannerClient($clientConfig); - $this->spannerClient->addMiddleware($middleware); $this->instanceAdminClient = $config['gapicSpannerInstanceAdminClient'] ?? new InstanceAdminClient($clientConfig); - $this->instanceAdminClient->addMiddleware($middleware); $this->databaseAdminClient = $config['gapicSpannerDatabaseAdminClient'] ?? new DatabaseAdminClient($clientConfig); + + // Add the SpannerMiddleware, which wraps API Exceptions, and adds + // Resource Prefix and LAR headers + $middleware = function (MiddlewareInterface $handler) { + return new SpannerMiddleware($handler); + }; + $this->spannerClient->addMiddleware($middleware); + $this->instanceAdminClient->addMiddleware($middleware); $this->databaseAdminClient->addMiddleware($middleware); + $this->projectName = InstanceAdminClient::projectName($this->projectId); } @@ -410,12 +415,11 @@ public function instanceConfigurations(array $options = []) $data['parent'] = $this->projectName; $request = $this->serializer->decodeMessage(new ListInstanceConfigsRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); return $this->buildListItemsIterator( [$this->instanceAdminClient, 'listInstanceConfigs'], $request, - $callOptions, + $callOptions + ['resource-prefix' => $this->projectName], function (array $config) { return $this->instanceConfiguration($config['name'], $config); }, @@ -486,14 +490,13 @@ public function instanceConfiguration($name, array $options = []) public function instanceConfigOperations(array $options = []) { [$data, $callOptions] = $this->splitOptionalArgs($options); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); $request = $this->serializer->decodeMessage(new ListInstanceConfigOperationsRequest(), $data); $request->setParent($this->projectName); return $this->buildLongRunningIterator( [$this->instanceAdminClient, 'listInstanceConfigOperations'], $request, - $callOptions, + $callOptions + ['resource-prefix' => $this->projectName], function (Operation $operation) { return $this->resumeOperation( $operation->getName(), @@ -598,12 +601,11 @@ public function instances(array $options = []) $data += ['filter' => '', 'parent' => $this->projectName]; $request = $this->serializer->decodeMessage(new ListInstancesRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->projectName); return $this->buildListItemsIterator( [$this->instanceAdminClient, 'listInstances'], $request, - $callOptions, + $callOptions + ['resource-prefix' => $this->projectName], function (array $instance) { $name = InstanceAdminClient::parseName($instance['name'])['instance']; return $this->instance($name, $instance); diff --git a/Spanner/src/TransactionalReadTrait.php b/Spanner/src/TransactionalReadTrait.php index b80415e3ce8..c9b29e72a21 100644 --- a/Spanner/src/TransactionalReadTrait.php +++ b/Spanner/src/TransactionalReadTrait.php @@ -26,7 +26,6 @@ trait TransactionalReadTrait { use TransactionConfigurationTrait; - // use RequestTrait; /** * @var Operation @@ -303,15 +302,12 @@ public function execute($sql, array $options = []) $this->directedReadOptions ?? [] ); - if ($this->context === SessionPoolInterface::CONTEXT_READWRITE) { - // add LAR header - $options['headers']['x-goog-spanner-route-to-leader'] = ['true']; - } - // Unsetting the internal flag unset($options['singleUse']); - $result = $this->operation->execute($this->session, $sql, $options); + $result = $this->operation->execute($this->session, $sql, $options + [ + 'route-to-leader' => $this->context === SessionPoolInterface::CONTEXT_READWRITE + ]); if (empty($this->id()) && $result->transaction()) { $this->setId($result->transaction()->id()); @@ -389,12 +385,9 @@ public function read($table, KeySet $keySet, array $columns, array $options = [] $this->directedReadOptions ?? [] ); - if ($this->context === SessionPoolInterface::CONTEXT_READWRITE) { - // add LAR header - $options['headers']['x-goog-spanner-route-to-leader'] = ['true']; - } - - $result = $this->operation->read($this->session, $table, $keySet, $columns, $options); + $result = $this->operation->read($this->session, $table, $keySet, $columns, $options + [ + 'route-to-leader' => $this->context === SessionPoolInterface::CONTEXT_READWRITE + ]); if (empty($this->id()) && $result->transaction()) { $this->setId($result->transaction()->id()); } diff --git a/Spanner/tests/Unit/DatabaseTest.php b/Spanner/tests/Unit/DatabaseTest.php index 35d355a3fd0..99c4037f8d9 100644 --- a/Spanner/tests/Unit/DatabaseTest.php +++ b/Spanner/tests/Unit/DatabaseTest.php @@ -1332,9 +1332,8 @@ public function testExecute() return $request->getSql() == $sql; }), Argument::that(function ($callOptions) { - $this->assertArrayHasKey('headers', $callOptions); - $this->assertArrayHasKey('x-goog-spanner-route-to-leader', $callOptions['headers']); - $this->assertEquals(['true'], $callOptions['headers']['x-goog-spanner-route-to-leader']); + $this->assertArrayHasKey('route-to-leader', $callOptions); + $this->assertEquals(true, $callOptions['route-to-leader']); return true; }) ) @@ -1428,9 +1427,8 @@ public function testExecutePartitionedUpdate() return true; }), Argument::that(function ($callOptions) { - $this->assertArrayHasKey('headers', $callOptions); - $this->assertArrayHasKey('x-goog-spanner-route-to-leader', $callOptions['headers']); - $this->assertEquals(['true'], $callOptions['headers']['x-goog-spanner-route-to-leader']); + $this->assertArrayHasKey('route-to-leader', $callOptions); + $this->assertEquals(true, $callOptions['route-to-leader']); return true; }) ) diff --git a/Spanner/tests/Unit/TransactionTest.php b/Spanner/tests/Unit/TransactionTest.php index ce4c18ad08d..bc00c7f3287 100644 --- a/Spanner/tests/Unit/TransactionTest.php +++ b/Spanner/tests/Unit/TransactionTest.php @@ -130,10 +130,7 @@ public function testExecute() return true; }), Argument::that(function (array $callOptions) { - $this->assertEquals( - $callOptions['headers']['x-goog-spanner-route-to-leader'], - ['true'] - ); + $this->assertEquals($callOptions['route-to-leader'], true); return true; }) ) @@ -256,10 +253,7 @@ public function testExecuteUpdateBatch() return true; }), Argument::that(function (array $callOptions) { - $this->assertEquals( - $callOptions['headers']['x-goog-spanner-route-to-leader'], - ['true'] - ); + $this->assertEquals($callOptions['route-to-leader'], true); return true; }) ) @@ -427,10 +421,8 @@ public function testRead() return true; }), Argument::that(function (array $callOptions) { - $this->assertEquals( - $callOptions['headers']['x-goog-spanner-route-to-leader'], - ['true'] - ); + $this->assertArrayHasKey('route-to-leader', $callOptions); + $this->assertEquals(true, $callOptions['route-to-leader']); return true; })