Skip to content
Open
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
74 changes: 74 additions & 0 deletions spanner/src/isolation_level.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/**
* Copyright 2026 Google 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.
*/

/**
* For instructions on how to run the full sample:
*
* @see https://github.com/GoogleCloudPlatform/php-docs-samples/tree/main/spanner/README.md
*/

namespace Google\Cloud\Samples\Spanner;

// [START spanner_isolation_level]
use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Transaction;
use Google\Cloud\Spanner\V1\TransactionOptions\IsolationLevel;

/**
* Shows how to run a Read Write transaction with isolation level options.
*
* Example:
* ```
* isolation_level($instanceId, $databaseId);
* ```
*
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
*/
function isolation_level(string $instanceId, string $databaseId): void
{
// The isolation level specified at the client-level will be applied to all
// RW transactions.
$spanner = new SpannerClient([
'isolationLevel' => IsolationLevel::SERIALIZABLE
]);
$instance = $spanner->instance($instanceId);
$database = $instance->database($databaseId);

// The isolation level specified at the request level takes precedence over
// the isolation level configured at the client level.
$database->runTransaction(function (Transaction $t) {
// Read an AlbumTitle.
$results = $t->execute('SELECT AlbumTitle from Albums WHERE SingerId = 1 and AlbumId = 1');
foreach ($results as $row) {
printf('Current Album Title: %s' . PHP_EOL, $row['AlbumTitle']);
}

// Update the AlbumTitle.
$rowCount = $t->executeUpdate('UPDATE Albums SET AlbumTitle = \'A New Title\' WHERE SingerId = 1 and AlbumId = 1');
printf('%d record(s) updated.' . PHP_EOL, $rowCount);
}, [
'transactionOptions' => [
'isolationLevel' => IsolationLevel::REPEATABLE_READ
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Cloud Spanner only supports SERIALIZABLE isolation for read-write transactions in the GoogleSQL dialect. REPEATABLE_READ is only supported for read-only transactions. Since runTransaction creates a read-write transaction and this snippet performs an UPDATE, using REPEATABLE_READ here will likely result in an error from the Spanner API. Consider using SERIALIZABLE or demonstrating this option in a read-only transaction context.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is not true. Cloud Spanner supports setting the isolation level to SERIALIZABLE or REPEATABLE_READ in both GSQL and PG dialects. It also supports setting the isolation level in both ReadWrite and ReadOnly transactions. It's a no-op to set it in ReadOnly transactions.

]
]);
}
// [END spanner_isolation_level]

// The following 2 lines are only needed to run the samples
require_once __DIR__ . '/../../testing/sample_helpers.php';
\Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv);
74 changes: 74 additions & 0 deletions spanner/src/read_lock_mode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/**
* Copyright 2026 Google 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.
*/

/**
* For instructions on how to run the full sample:
*
* @see https://github.com/GoogleCloudPlatform/php-docs-samples/tree/main/spanner/README.md
*/

namespace Google\Cloud\Samples\Spanner;

// [START spanner_read_lock_mode]
use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Transaction;
use Google\Cloud\Spanner\V1\TransactionOptions\ReadWrite\ReadLockMode;

/**
* Shows how to run a Read Write transaction with read lock mode options.
*
* Example:
* ```
* read_lock_mode($instanceId, $databaseId);
* ```
*
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
*/
function read_lock_mode(string $instanceId, string $databaseId): void
{
// The read lock mode specified at the client-level will be applied to all
// RW transactions.
$spanner = new SpannerClient([
'readLockMode' => ReadLockMode::OPTIMISTIC
]);
$instance = $spanner->instance($instanceId);
$database = $instance->database($databaseId);

// The read lock mode specified at the request level takes precedence over
// the read lock mode configured at the client level.
$database->runTransaction(function (Transaction $t) {
// Read an AlbumTitle.
$results = $t->execute('SELECT AlbumTitle from Albums WHERE SingerId = 2 and AlbumId = 1');
foreach ($results as $row) {
printf('Current Album Title: %s' . PHP_EOL, $row['AlbumTitle']);
}

// Update the AlbumTitle.
$rowCount = $t->executeUpdate('UPDATE Albums SET AlbumTitle = \'A New Title\' WHERE SingerId = 2 and AlbumId = 1');
printf('%d record(s) updated.' . PHP_EOL, $rowCount);
}, [
'transactionOptions' => [
'readLockMode' => ReadLockMode::PESSIMISTIC
]
Comment on lines +65 to +67
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The readLockMode option must be nested under the readWrite key within transactionOptions to correctly map to the underlying Protobuf structure (transaction_options.read_write.read_lock_mode). Without this nesting, the option will be ignored by the library.

        'transactionOptions' => [
            'readWrite' => [
                'readLockMode' => ReadLockMode::PESSIMISTIC
            ]
        ]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is necessary when setting it in the grpc API but not at the client-library level. See the unit tests for an example of setting readLockMode in this way.

]);
}
// [END spanner_read_lock_mode]

// The following 2 lines are only needed to run the samples
require_once __DIR__ . '/../../testing/sample_helpers.php';
\Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv);
20 changes: 20 additions & 0 deletions spanner/test/spannerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,26 @@ public function testReadWriteTransaction()
$this->assertStringContainsString('Transaction complete.', $output);
}

/**
* @depends testUpdateData
*/
public function testReadLockMode()
{
$output = $this->runFunctionSnippet('read_lock_mode');
$this->assertStringContainsString('Current Album Title:', $output);
$this->assertStringContainsString('record(s) updated.', $output);
}

/**
* @depends testUpdateData
*/
public function testIsolationLevel()
{
$output = $this->runFunctionSnippet('isolation_level');
$this->assertStringContainsString('Current Album Title:', $output);
$this->assertStringContainsString('record(s) updated.', $output);
}

/**
* @depends testAddColumn
*/
Expand Down
Loading