diff --git a/spanner/spanner_snippets/spanner/integration_test.go b/spanner/spanner_snippets/spanner/integration_test.go index 47c2579ef6..f09032ffa2 100644 --- a/spanner/spanner_snippets/spanner/integration_test.go +++ b/spanner/spanner_snippets/spanner/integration_test.go @@ -167,6 +167,7 @@ func TestSample(t *testing.T) { mustRunSample(t, createDatabase, dbName, "failed to create a database") runSample(t, createClients, dbName, "failed to create clients") runSample(t, write, dbName, "failed to insert data") + runSample(t, applyAtLeastOnce, dbName, "failed to apply mutations at least once") out = runSample(t, batchWrite, dbName, "failed to write data using BatchWrite") assertNotContains(t, out, "could not be applied with error") runSampleWithContext(ctx, t, addNewColumn, dbName, "failed to add new column") diff --git a/spanner/spanner_snippets/spanner/spanner_apply_at_least_once.go b/spanner/spanner_snippets/spanner/spanner_apply_at_least_once.go new file mode 100644 index 0000000000..786e5ab250 --- /dev/null +++ b/spanner/spanner_snippets/spanner/spanner_apply_at_least_once.go @@ -0,0 +1,53 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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. + +package spanner + +import ( + "context" + "io" + + "cloud.google.com/go/spanner" +) + +// applyAtLeastOnce shows how to apply mutations in a single-use write transaction. +// Single-use write transactions have no replay protection. +func applyAtLeastOnce(w io.Writer, db string) error { + ctx := context.Background() + client, err := spanner.NewClient(ctx, db) + if err != nil { + return err + } + defer client.Close() + + singerColumns := []string{"SingerId", "FirstName", "LastName"} + albumColumns := []string{"SingerId", "AlbumId", "AlbumTitle"} + m := []*spanner.Mutation{ + spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{1, "Marc", "Richards"}), + spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{2, "Catalina", "Smith"}), + spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{3, "Alice", "Trentor"}), + spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{4, "Lea", "Martin"}), + spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{5, "David", "Lomond"}), + spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{1, 1, "Total Junk"}), + spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{1, 2, "Go, Go, Go"}), + spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{2, 1, "Green"}), + spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{2, 2, "Forever Hold Your Peace"}), + spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{2, 3, "Terrified"}), + } + // The spanner.ApplyAtLeastOnce() option instructs the client to use a single Commit request + // to apply the mutations, instead of first executing a BeginTransaction RPC. This reduces + // the number of round-trips to Spanner, but also means that there is no replay protection. + _, err = client.Apply(ctx, m, spanner.ApplyAtLeastOnce()) + return err +}