activerecord-spanner-adapter 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/CODEOWNERS +7 -0
- data/.github/sync-repo-settings.yaml +16 -0
- data/.github/workflows/acceptance-tests-on-emulator.yaml +45 -0
- data/.github/workflows/acceptance-tests-on-production.yaml +49 -0
- data/.github/workflows/ci.yaml +33 -0
- data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +52 -0
- data/.github/workflows/nightly-acceptance-tests-on-production.yaml +35 -0
- data/.github/workflows/nightly-unit-tests.yaml +40 -0
- data/.github/workflows/release-please-label.yml +25 -0
- data/.github/workflows/release-please.yml +39 -0
- data/.github/workflows/rubocop.yaml +31 -0
- data/.gitignore +67 -5
- data/.kokoro/populate-secrets.sh +77 -0
- data/.kokoro/release.cfg +33 -0
- data/.kokoro/release.sh +15 -0
- data/.kokoro/trampoline_v2.sh +489 -0
- data/.rubocop.yml +46 -0
- data/.toys/release.rb +18 -0
- data/.trampolinerc +48 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +55 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +79 -0
- data/Gemfile +9 -4
- data/LICENSE +6 -6
- data/README.md +66 -30
- data/Rakefile +79 -3
- data/SECURITY.md +7 -0
- data/acceptance/cases/associations/has_many_associations_test.rb +119 -0
- data/acceptance/cases/associations/has_many_through_associations_test.rb +63 -0
- data/acceptance/cases/associations/has_one_associations_test.rb +79 -0
- data/acceptance/cases/associations/has_one_through_associations_test.rb +98 -0
- data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +211 -0
- data/acceptance/cases/migration/change_schema_test.rb +433 -0
- data/acceptance/cases/migration/change_table_test.rb +115 -0
- data/acceptance/cases/migration/column_attributes_test.rb +122 -0
- data/acceptance/cases/migration/column_positioning_test.rb +48 -0
- data/acceptance/cases/migration/columns_test.rb +201 -0
- data/acceptance/cases/migration/command_recorder_test.rb +406 -0
- data/acceptance/cases/migration/create_join_table_test.rb +216 -0
- data/acceptance/cases/migration/ddl_batching_test.rb +80 -0
- data/acceptance/cases/migration/foreign_key_test.rb +297 -0
- data/acceptance/cases/migration/index_test.rb +211 -0
- data/acceptance/cases/migration/references_foreign_key_test.rb +259 -0
- data/acceptance/cases/migration/references_index_test.rb +135 -0
- data/acceptance/cases/migration/references_statements_test.rb +166 -0
- data/acceptance/cases/migration/rename_column_test.rb +96 -0
- data/acceptance/cases/models/calculation_query_test.rb +128 -0
- data/acceptance/cases/models/generated_column_test.rb +126 -0
- data/acceptance/cases/models/mutation_test.rb +122 -0
- data/acceptance/cases/models/query_test.rb +171 -0
- data/acceptance/cases/sessions/session_not_found_test.rb +121 -0
- data/acceptance/cases/transactions/optimistic_locking_test.rb +141 -0
- data/acceptance/cases/transactions/read_only_transactions_test.rb +130 -0
- data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
- data/acceptance/cases/type/all_types_test.rb +172 -0
- data/acceptance/cases/type/binary_test.rb +59 -0
- data/acceptance/cases/type/boolean_test.rb +31 -0
- data/acceptance/cases/type/date_test.rb +32 -0
- data/acceptance/cases/type/date_time_test.rb +30 -0
- data/acceptance/cases/type/float_test.rb +27 -0
- data/acceptance/cases/type/integer_test.rb +44 -0
- data/acceptance/cases/type/json_test.rb +34 -0
- data/acceptance/cases/type/numeric_test.rb +27 -0
- data/acceptance/cases/type/string_test.rb +79 -0
- data/acceptance/cases/type/text_test.rb +30 -0
- data/acceptance/cases/type/time_test.rb +87 -0
- data/acceptance/models/account.rb +13 -0
- data/acceptance/models/address.rb +9 -0
- data/acceptance/models/album.rb +12 -0
- data/acceptance/models/all_types.rb +8 -0
- data/acceptance/models/author.rb +11 -0
- data/acceptance/models/club.rb +12 -0
- data/acceptance/models/comment.rb +9 -0
- data/acceptance/models/customer.rb +9 -0
- data/acceptance/models/department.rb +9 -0
- data/acceptance/models/firm.rb +10 -0
- data/acceptance/models/member.rb +13 -0
- data/acceptance/models/member_type.rb +9 -0
- data/acceptance/models/membership.rb +10 -0
- data/acceptance/models/organization.rb +9 -0
- data/acceptance/models/post.rb +10 -0
- data/acceptance/models/singer.rb +10 -0
- data/acceptance/models/track.rb +20 -0
- data/acceptance/models/transaction.rb +9 -0
- data/acceptance/schema/schema.rb +147 -0
- data/acceptance/test_helper.rb +261 -0
- data/activerecord-spanner-adapter.gemspec +32 -17
- data/assets/solidus-db.png +0 -0
- data/benchmarks/README.md +17 -0
- data/benchmarks/Rakefile +14 -0
- data/benchmarks/application.rb +308 -0
- data/benchmarks/config/database.yml +8 -0
- data/benchmarks/config/environment.rb +12 -0
- data/benchmarks/db/migrate/01_create_tables.rb +25 -0
- data/benchmarks/db/schema.rb +29 -0
- data/benchmarks/models/album.rb +9 -0
- data/benchmarks/models/singer.rb +9 -0
- data/bin/console +6 -7
- data/examples/rails/README.md +262 -0
- data/examples/snippets/README.md +29 -0
- data/examples/snippets/Rakefile +57 -0
- data/examples/snippets/array-data-type/README.md +45 -0
- data/examples/snippets/array-data-type/Rakefile +13 -0
- data/examples/snippets/array-data-type/application.rb +45 -0
- data/examples/snippets/array-data-type/config/database.yml +8 -0
- data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
- data/examples/snippets/array-data-type/db/schema.rb +26 -0
- data/examples/snippets/array-data-type/db/seeds.rb +5 -0
- data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
- data/examples/snippets/bin/create_emulator_instance.rb +18 -0
- data/examples/snippets/bulk-insert/README.md +21 -0
- data/examples/snippets/bulk-insert/Rakefile +13 -0
- data/examples/snippets/bulk-insert/application.rb +64 -0
- data/examples/snippets/bulk-insert/config/database.yml +8 -0
- data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/bulk-insert/db/schema.rb +26 -0
- data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
- data/examples/snippets/bulk-insert/models/album.rb +9 -0
- data/examples/snippets/bulk-insert/models/singer.rb +9 -0
- data/examples/snippets/commit-timestamp/README.md +18 -0
- data/examples/snippets/commit-timestamp/Rakefile +13 -0
- data/examples/snippets/commit-timestamp/application.rb +53 -0
- data/examples/snippets/commit-timestamp/config/database.yml +8 -0
- data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
- data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
- data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
- data/examples/snippets/commit-timestamp/models/album.rb +9 -0
- data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
- data/examples/snippets/config/environment.rb +21 -0
- data/examples/snippets/create-records/README.md +12 -0
- data/examples/snippets/create-records/Rakefile +13 -0
- data/examples/snippets/create-records/application.rb +42 -0
- data/examples/snippets/create-records/config/database.yml +8 -0
- data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/create-records/db/schema.rb +26 -0
- data/examples/snippets/create-records/db/seeds.rb +5 -0
- data/examples/snippets/create-records/models/album.rb +9 -0
- data/examples/snippets/create-records/models/singer.rb +9 -0
- data/examples/snippets/date-data-type/README.md +19 -0
- data/examples/snippets/date-data-type/Rakefile +13 -0
- data/examples/snippets/date-data-type/application.rb +35 -0
- data/examples/snippets/date-data-type/config/database.yml +8 -0
- data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
- data/examples/snippets/date-data-type/db/schema.rb +21 -0
- data/examples/snippets/date-data-type/db/seeds.rb +16 -0
- data/examples/snippets/date-data-type/models/singer.rb +8 -0
- data/examples/snippets/generated-column/README.md +41 -0
- data/examples/snippets/generated-column/Rakefile +13 -0
- data/examples/snippets/generated-column/application.rb +37 -0
- data/examples/snippets/generated-column/config/database.yml +8 -0
- data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
- data/examples/snippets/generated-column/db/schema.rb +21 -0
- data/examples/snippets/generated-column/db/seeds.rb +18 -0
- data/examples/snippets/generated-column/models/singer.rb +8 -0
- data/examples/snippets/hints/README.md +19 -0
- data/examples/snippets/hints/Rakefile +13 -0
- data/examples/snippets/hints/application.rb +47 -0
- data/examples/snippets/hints/config/database.yml +8 -0
- data/examples/snippets/hints/db/migrate/01_create_tables.rb +23 -0
- data/examples/snippets/hints/db/schema.rb +28 -0
- data/examples/snippets/hints/db/seeds.rb +29 -0
- data/examples/snippets/hints/models/album.rb +9 -0
- data/examples/snippets/hints/models/singer.rb +9 -0
- data/examples/snippets/migrations/README.md +43 -0
- data/examples/snippets/migrations/Rakefile +13 -0
- data/examples/snippets/migrations/application.rb +26 -0
- data/examples/snippets/migrations/config/database.yml +8 -0
- data/examples/snippets/migrations/db/migrate/01_create_tables.rb +28 -0
- data/examples/snippets/migrations/db/schema.rb +33 -0
- data/examples/snippets/migrations/db/seeds.rb +5 -0
- data/examples/snippets/migrations/models/album.rb +10 -0
- data/examples/snippets/migrations/models/singer.rb +10 -0
- data/examples/snippets/migrations/models/track.rb +9 -0
- data/examples/snippets/mutations/README.md +34 -0
- data/examples/snippets/mutations/Rakefile +13 -0
- data/examples/snippets/mutations/application.rb +47 -0
- data/examples/snippets/mutations/config/database.yml +8 -0
- data/examples/snippets/mutations/db/migrate/01_create_tables.rb +22 -0
- data/examples/snippets/mutations/db/schema.rb +27 -0
- data/examples/snippets/mutations/db/seeds.rb +25 -0
- data/examples/snippets/mutations/models/album.rb +9 -0
- data/examples/snippets/mutations/models/singer.rb +9 -0
- data/examples/snippets/optimistic-locking/README.md +12 -0
- data/examples/snippets/optimistic-locking/Rakefile +13 -0
- data/examples/snippets/optimistic-locking/application.rb +48 -0
- data/examples/snippets/optimistic-locking/config/database.yml +8 -0
- data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +26 -0
- data/examples/snippets/optimistic-locking/db/schema.rb +29 -0
- data/examples/snippets/optimistic-locking/db/seeds.rb +25 -0
- data/examples/snippets/optimistic-locking/models/album.rb +9 -0
- data/examples/snippets/optimistic-locking/models/singer.rb +9 -0
- data/examples/snippets/partitioned-dml/README.md +16 -0
- data/examples/snippets/partitioned-dml/Rakefile +13 -0
- data/examples/snippets/partitioned-dml/application.rb +48 -0
- data/examples/snippets/partitioned-dml/config/database.yml +8 -0
- data/examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/partitioned-dml/db/schema.rb +26 -0
- data/examples/snippets/partitioned-dml/db/seeds.rb +29 -0
- data/examples/snippets/partitioned-dml/models/album.rb +9 -0
- data/examples/snippets/partitioned-dml/models/singer.rb +9 -0
- data/examples/snippets/quickstart/README.md +26 -0
- data/examples/snippets/quickstart/Rakefile +13 -0
- data/examples/snippets/quickstart/application.rb +51 -0
- data/examples/snippets/quickstart/config/database.yml +8 -0
- data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/quickstart/db/schema.rb +26 -0
- data/examples/snippets/quickstart/db/seeds.rb +24 -0
- data/examples/snippets/quickstart/models/album.rb +9 -0
- data/examples/snippets/quickstart/models/singer.rb +9 -0
- data/examples/snippets/read-only-transactions/README.md +13 -0
- data/examples/snippets/read-only-transactions/Rakefile +13 -0
- data/examples/snippets/read-only-transactions/application.rb +77 -0
- data/examples/snippets/read-only-transactions/config/database.yml +8 -0
- data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
- data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
- data/examples/snippets/read-only-transactions/models/album.rb +9 -0
- data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
- data/examples/snippets/read-write-transactions/README.md +12 -0
- data/examples/snippets/read-write-transactions/Rakefile +13 -0
- data/examples/snippets/read-write-transactions/application.rb +39 -0
- data/examples/snippets/read-write-transactions/config/database.yml +8 -0
- data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
- data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
- data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
- data/examples/snippets/read-write-transactions/models/album.rb +9 -0
- data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
- data/examples/snippets/stale-reads/README.md +27 -0
- data/examples/snippets/stale-reads/Rakefile +13 -0
- data/examples/snippets/stale-reads/application.rb +63 -0
- data/examples/snippets/stale-reads/config/database.yml +8 -0
- data/examples/snippets/stale-reads/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/stale-reads/db/schema.rb +26 -0
- data/examples/snippets/stale-reads/db/seeds.rb +24 -0
- data/examples/snippets/stale-reads/models/album.rb +9 -0
- data/examples/snippets/stale-reads/models/singer.rb +9 -0
- data/examples/snippets/timestamp-data-type/README.md +17 -0
- data/examples/snippets/timestamp-data-type/Rakefile +13 -0
- data/examples/snippets/timestamp-data-type/application.rb +42 -0
- data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
- data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
- data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
- data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
- data/examples/solidus/README.md +172 -0
- data/lib/active_record/connection_adapters/spanner/database_statements.rb +244 -266
- data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
- data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
- data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
- data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
- data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/spanner_adapter.rb +185 -78
- data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
- data/lib/active_record/type/spanner/array.rb +32 -0
- data/lib/active_record/type/spanner/bytes.rb +26 -0
- data/lib/active_record/type/spanner/spanner_active_record_converter.rb +33 -0
- data/lib/active_record/type/spanner/time.rb +37 -0
- data/lib/activerecord-spanner-adapter.rb +23 -0
- data/lib/activerecord_spanner_adapter/base.rb +238 -0
- data/lib/activerecord_spanner_adapter/connection.rb +350 -0
- data/lib/activerecord_spanner_adapter/errors.rb +13 -0
- data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
- data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
- data/lib/activerecord_spanner_adapter/index.rb +80 -0
- data/lib/activerecord_spanner_adapter/information_schema.rb +262 -0
- data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
- data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
- data/lib/activerecord_spanner_adapter/table.rb +61 -0
- data/lib/activerecord_spanner_adapter/transaction.rb +154 -0
- data/lib/activerecord_spanner_adapter/version.rb +9 -0
- data/lib/arel/visitors/spanner.rb +111 -0
- data/lib/spanner_client_ext.rb +107 -0
- data/renovate.json +5 -0
- metadata +405 -34
- data/.travis.yml +0 -5
- data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
- data/lib/active_record/connection_adapters/spanner.rb +0 -10
- data/lib/activerecord-spanner-adapter/version.rb +0 -3
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require_relative "../config/environment"
|
|
8
|
+
require "sinatra/activerecord/rake"
|
|
9
|
+
|
|
10
|
+
desc "Sample showing how to execute a read/write transaction using Mutations instead of DML with ActiveRecord."
|
|
11
|
+
task :run do
|
|
12
|
+
Dir.chdir("..") { sh "bundle exec rake run[mutations]" }
|
|
13
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require "io/console"
|
|
8
|
+
require_relative "../config/environment"
|
|
9
|
+
require_relative "models/singer"
|
|
10
|
+
require_relative "models/album"
|
|
11
|
+
|
|
12
|
+
class Application
|
|
13
|
+
def self.run # rubocop:disable Metrics/AbcSize
|
|
14
|
+
from_album = nil
|
|
15
|
+
to_album = nil
|
|
16
|
+
# This transaction uses Mutations instead of DML. That means that any data changes are not readable during
|
|
17
|
+
# the transaction itself, as the mutations are buffered locally in the client and only sent to Spanner
|
|
18
|
+
# when the transaction is committed.
|
|
19
|
+
ActiveRecord::Base.transaction isolation: :buffered_mutations do
|
|
20
|
+
# Transfer a marketing budget of 10,000 from one album to another.
|
|
21
|
+
from_album = Album.all.sample
|
|
22
|
+
to_album = Album.where.not(id: from_album.id).sample
|
|
23
|
+
|
|
24
|
+
puts ""
|
|
25
|
+
puts "Transferring 10,000 marketing budget from #{from_album.title} (#{from_album.marketing_budget}) "\
|
|
26
|
+
"to #{to_album.title} (#{to_album.marketing_budget})"
|
|
27
|
+
from_album.update marketing_budget: from_album.marketing_budget - 10000
|
|
28
|
+
to_album.update marketing_budget: to_album.marketing_budget + 10000
|
|
29
|
+
|
|
30
|
+
# The above change is not sent to the database before the transaction is committed.
|
|
31
|
+
puts ""
|
|
32
|
+
puts "Budgets before commit:"
|
|
33
|
+
puts "Marketing budget #{from_album.title}: #{from_album.reload.marketing_budget}"
|
|
34
|
+
puts "Marketing budget #{to_album.title}: #{to_album.reload.marketing_budget}"
|
|
35
|
+
end
|
|
36
|
+
puts ""
|
|
37
|
+
puts "Budgets after commit:"
|
|
38
|
+
puts "Marketing budget #{from_album.title}: #{from_album.reload.marketing_budget}"
|
|
39
|
+
puts "Marketing budget #{to_album.title}: #{to_album.reload.marketing_budget}"
|
|
40
|
+
|
|
41
|
+
puts ""
|
|
42
|
+
puts "Press any key to end the application"
|
|
43
|
+
STDIN.getch
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Application.run
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
class CreateTables < ActiveRecord::Migration[6.0]
|
|
8
|
+
def change
|
|
9
|
+
connection.ddl_batch do
|
|
10
|
+
create_table :singers do |t|
|
|
11
|
+
t.string :first_name
|
|
12
|
+
t.string :last_name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
create_table :albums do |t|
|
|
16
|
+
t.string :title
|
|
17
|
+
t.numeric :marketing_budget
|
|
18
|
+
t.references :singer, index: false, foreign_key: true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
4
|
+
#
|
|
5
|
+
# This file is the source Rails uses to define your schema when running `rails
|
|
6
|
+
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
|
7
|
+
# be faster and is potentially less error prone than running all of your
|
|
8
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
|
9
|
+
# migrations use external dependencies or application code.
|
|
10
|
+
#
|
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Schema.define(version: 1) do
|
|
14
|
+
|
|
15
|
+
create_table "albums", force: :cascade do |t|
|
|
16
|
+
t.string "title"
|
|
17
|
+
t.decimal "marketing_budget"
|
|
18
|
+
t.integer "singer_id", limit: 8
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
create_table "singers", force: :cascade do |t|
|
|
22
|
+
t.string "first_name"
|
|
23
|
+
t.string "last_name"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
add_foreign_key "albums", "singers"
|
|
27
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require_relative "../../config/environment.rb"
|
|
8
|
+
require_relative "../models/singer"
|
|
9
|
+
require_relative "../models/album"
|
|
10
|
+
|
|
11
|
+
first_names = ["Pete", "Alice", "John", "Ethel", "Trudy", "Naomi", "Wendy", "Ruben", "Thomas", "Elly"]
|
|
12
|
+
last_names = ["Wendelson", "Allison", "Peterson", "Johnson", "Henderson", "Ericsson", "Aronson", "Tennet", "Courtou"]
|
|
13
|
+
|
|
14
|
+
adjectives = ["daily", "happy", "blue", "generous", "cooked", "bad", "open"]
|
|
15
|
+
nouns = ["windows", "potatoes", "bank", "street", "tree", "glass", "bottle"]
|
|
16
|
+
budgets = [15000, 25000, 10000, 20000, 30000, 12000, 13000]
|
|
17
|
+
|
|
18
|
+
5.times do
|
|
19
|
+
Singer.create first_name: first_names.sample, last_name: last_names.sample
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
20.times do
|
|
23
|
+
singer_id = Singer.all.sample.id
|
|
24
|
+
Album.create title: "#{adjectives.sample} #{nouns.sample}", marketing_budget: budgets.sample, singer_id: singer_id
|
|
25
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Sample - Optimistic Locking
|
|
2
|
+
|
|
3
|
+
This example shows how to use optimistic locking with the Spanner ActiveRecord adapter.
|
|
4
|
+
|
|
5
|
+
The sample will automatically start a Spanner Emulator in a docker container and execute the sample
|
|
6
|
+
against that emulator. The emulator will automatically be stopped when the application finishes.
|
|
7
|
+
|
|
8
|
+
Run the application with the command
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
bundle exec rake run
|
|
12
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require_relative "../config/environment"
|
|
8
|
+
require "sinatra/activerecord/rake"
|
|
9
|
+
|
|
10
|
+
desc "Sample showing how to use optimistic locking with the Cloud Spanner ActiveRecord adapter."
|
|
11
|
+
task :run do
|
|
12
|
+
Dir.chdir("..") { sh "bundle exec rake run[optimistic-locking]" }
|
|
13
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require "io/console"
|
|
8
|
+
require_relative "../config/environment"
|
|
9
|
+
require_relative "models/singer"
|
|
10
|
+
require_relative "models/album"
|
|
11
|
+
|
|
12
|
+
class Application
|
|
13
|
+
def self.run
|
|
14
|
+
# Get a random singer and then update the singer in a separate thread.
|
|
15
|
+
# This simulates a concurrent update of the same record by two different processes.
|
|
16
|
+
singer = Singer.all.sample
|
|
17
|
+
|
|
18
|
+
puts ""
|
|
19
|
+
puts "Singer #{singer.first_name} #{singer.last_name} with version #{singer.lock_version} loaded"
|
|
20
|
+
|
|
21
|
+
t = Thread.new do
|
|
22
|
+
# Load the singer in the separate thread into a separate variable.
|
|
23
|
+
singer2 = Singer.find singer.id
|
|
24
|
+
singer2.update last_name: "Rashford"
|
|
25
|
+
puts ""
|
|
26
|
+
puts "Updated the last name of the singer to #{singer2.last_name}"
|
|
27
|
+
end
|
|
28
|
+
t.join
|
|
29
|
+
|
|
30
|
+
# Now try to update the singer in the main thread. This will fail, as the lock_version number has been increased
|
|
31
|
+
# by the update in the separate thread.
|
|
32
|
+
begin
|
|
33
|
+
singer.update last_name: "Drake"
|
|
34
|
+
rescue ActiveRecord::StaleObjectError
|
|
35
|
+
puts ""
|
|
36
|
+
puts "Updating the singer in the main thread failed with a StaleObjectError"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
singer.reload
|
|
40
|
+
puts "Reloaded singer data: #{singer.first_name} #{singer.last_name}, version: #{singer.lock_version}"
|
|
41
|
+
|
|
42
|
+
puts ""
|
|
43
|
+
puts "Press any key to end the application"
|
|
44
|
+
STDIN.getch
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Application.run
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
class CreateTables < ActiveRecord::Migration[6.0]
|
|
8
|
+
def change
|
|
9
|
+
connection.ddl_batch do
|
|
10
|
+
create_table :singers do |t|
|
|
11
|
+
t.string :first_name
|
|
12
|
+
t.string :last_name
|
|
13
|
+
# `lock_version` is the default name for an optimistic lock version column in ActiveRecord.
|
|
14
|
+
t.integer :lock_version
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
create_table :albums do |t|
|
|
18
|
+
t.string :title
|
|
19
|
+
t.numeric :marketing_budget
|
|
20
|
+
t.references :singer, index: false, foreign_key: true
|
|
21
|
+
# `lock_version` is the default name for an optimistic lock version column in ActiveRecord.
|
|
22
|
+
t.integer :lock_version
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
4
|
+
#
|
|
5
|
+
# This file is the source Rails uses to define your schema when running `rails
|
|
6
|
+
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
|
7
|
+
# be faster and is potentially less error prone than running all of your
|
|
8
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
|
9
|
+
# migrations use external dependencies or application code.
|
|
10
|
+
#
|
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Schema.define(version: 1) do
|
|
14
|
+
|
|
15
|
+
create_table "albums", force: :cascade do |t|
|
|
16
|
+
t.string "title"
|
|
17
|
+
t.decimal "marketing_budget"
|
|
18
|
+
t.integer "singer_id", limit: 8
|
|
19
|
+
t.integer "lock_version", limit: 8
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
create_table "singers", force: :cascade do |t|
|
|
23
|
+
t.string "first_name"
|
|
24
|
+
t.string "last_name"
|
|
25
|
+
t.integer "lock_version", limit: 8
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
add_foreign_key "albums", "singers"
|
|
29
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require_relative "../../config/environment.rb"
|
|
8
|
+
require_relative "../models/singer"
|
|
9
|
+
require_relative "../models/album"
|
|
10
|
+
|
|
11
|
+
first_names = ["Pete", "Alice", "John", "Ethel", "Trudy", "Naomi", "Wendy", "Ruben", "Thomas", "Elly"]
|
|
12
|
+
last_names = ["Wendelson", "Allison", "Peterson", "Johnson", "Henderson", "Ericsson", "Aronson", "Tennet", "Courtou"]
|
|
13
|
+
|
|
14
|
+
adjectives = ["daily", "happy", "blue", "generous", "cooked", "bad", "open"]
|
|
15
|
+
nouns = ["windows", "potatoes", "bank", "street", "tree", "glass", "bottle"]
|
|
16
|
+
budgets = [15000, 25000, 10000, 20000, 30000, 12000, 13000]
|
|
17
|
+
|
|
18
|
+
5.times do
|
|
19
|
+
Singer.create first_name: first_names.sample, last_name: last_names.sample
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
20.times do
|
|
23
|
+
singer_id = Singer.all.sample.id
|
|
24
|
+
Album.create title: "#{adjectives.sample} #{nouns.sample}", marketing_budget: budgets.sample, singer_id: singer_id
|
|
25
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Sample - Partitioned DML
|
|
2
|
+
|
|
3
|
+
This example shows how to use Partitioned DML with the Spanner ActiveRecord adapter.
|
|
4
|
+
|
|
5
|
+
See https://cloud.google.com/spanner/docs/dml-partitioned for more information on Partitioned DML.
|
|
6
|
+
|
|
7
|
+
## Running the Sample
|
|
8
|
+
|
|
9
|
+
The sample will automatically start a Spanner Emulator in a docker container and execute the sample
|
|
10
|
+
against that emulator. The emulator will automatically be stopped when the application finishes.
|
|
11
|
+
|
|
12
|
+
Run the application with the command
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bundle exec rake run
|
|
16
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require_relative "../config/environment"
|
|
8
|
+
require "sinatra/activerecord/rake"
|
|
9
|
+
|
|
10
|
+
desc "Sample showing how to work with Partitioned DML in ActiveRecord."
|
|
11
|
+
task :run do
|
|
12
|
+
Dir.chdir("..") { sh "bundle exec rake run[partitioned-dml]" }
|
|
13
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require "io/console"
|
|
8
|
+
require_relative "../config/environment"
|
|
9
|
+
require_relative "models/singer"
|
|
10
|
+
require_relative "models/album"
|
|
11
|
+
|
|
12
|
+
class Application
|
|
13
|
+
def self.run
|
|
14
|
+
singer_count = Singer.all.count
|
|
15
|
+
album_count = Album.all.count
|
|
16
|
+
puts ""
|
|
17
|
+
puts "Singers in the database: #{singer_count}"
|
|
18
|
+
puts "Albums in the database: #{album_count}"
|
|
19
|
+
|
|
20
|
+
puts ""
|
|
21
|
+
puts "Deleting all albums in the database using Partitioned DML"
|
|
22
|
+
# Note that a Partitioned DML transaction can contain ONLY ONE DML statement.
|
|
23
|
+
# If we want to delete all data in two different tables, we need to do so in two different PDML transactions.
|
|
24
|
+
Album.transaction isolation: :pdml do
|
|
25
|
+
count = Album.delete_all
|
|
26
|
+
puts "Deleted #{count} albums"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
puts ""
|
|
30
|
+
puts "Deleting all singers in the database using Partitioned DML"
|
|
31
|
+
Singer.transaction isolation: :pdml do
|
|
32
|
+
count = Singer.delete_all
|
|
33
|
+
puts "Deleted #{count} singers"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
singer_count = Singer.all.count
|
|
37
|
+
album_count = Album.all.count
|
|
38
|
+
puts ""
|
|
39
|
+
puts "Singers in the database: #{singer_count}"
|
|
40
|
+
puts "Albums in the database: #{album_count}"
|
|
41
|
+
|
|
42
|
+
puts ""
|
|
43
|
+
puts "Press any key to end the application"
|
|
44
|
+
STDIN.getch
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Application.run
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
class CreateTables < ActiveRecord::Migration[6.0]
|
|
8
|
+
def change
|
|
9
|
+
connection.ddl_batch do
|
|
10
|
+
create_table :singers do |t|
|
|
11
|
+
t.string :first_name, limit: 100
|
|
12
|
+
t.string :last_name, limit: 200, null: false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
create_table :albums do |t|
|
|
16
|
+
t.string :title
|
|
17
|
+
t.references :singer, index: false, foreign_key: true
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
4
|
+
#
|
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
|
7
|
+
# be faster and is potentially less error prone than running all of your
|
|
8
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
|
9
|
+
# migrations use external dependencies or application code.
|
|
10
|
+
#
|
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Schema.define(version: 1) do
|
|
14
|
+
|
|
15
|
+
create_table "albums", id: { limit: 8 }, force: :cascade do |t|
|
|
16
|
+
t.string "title"
|
|
17
|
+
t.integer "singer_id", limit: 8
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
create_table "singers", id: { limit: 8 }, force: :cascade do |t|
|
|
21
|
+
t.string "first_name", limit: 100
|
|
22
|
+
t.string "last_name", limit: 200, null: false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
add_foreign_key "albums", "singers"
|
|
26
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require_relative "../../config/environment.rb"
|
|
8
|
+
require_relative "../models/singer"
|
|
9
|
+
require_relative "../models/album"
|
|
10
|
+
|
|
11
|
+
first_names = %w[Pete Alice John Ethel Trudy Naomi Wendy Ruben Thomas Elly]
|
|
12
|
+
last_names = %w[Wendelson Allison Peterson Johnson Henderson Ericsson Aronson Tennet Courtou]
|
|
13
|
+
|
|
14
|
+
adjectives = %w[daily happy blue generous cooked bad open]
|
|
15
|
+
nouns = %w[windows potatoes bank street tree glass bottle]
|
|
16
|
+
|
|
17
|
+
# This ensures all the records are inserted using one read/write transaction that will use mutations instead of DML.
|
|
18
|
+
ActiveRecord::Base.transaction isolation: :buffered_mutations do
|
|
19
|
+
singers = []
|
|
20
|
+
5.times do
|
|
21
|
+
singers << Singer.create(first_name: first_names.sample, last_name: last_names.sample)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
albums = []
|
|
25
|
+
20.times do
|
|
26
|
+
singer = singers.sample
|
|
27
|
+
albums << Album.create(title: "#{adjectives.sample} #{nouns.sample}", singer: singer)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Sample - Quickstart
|
|
2
|
+
|
|
3
|
+
This sample shows how to create and use a very simple Cloud Spanner database with ActiveRecord.
|
|
4
|
+
The database consists of two tables:
|
|
5
|
+
- Singers
|
|
6
|
+
- Albums
|
|
7
|
+
|
|
8
|
+
Albums references Singers with a [foreign key](https://cloud.google.com/spanner/docs/foreign-keys/overview).
|
|
9
|
+
|
|
10
|
+
The sample will automatically start a Spanner Emulator in a docker container and execute the sample
|
|
11
|
+
against that emulator. The emulator will automatically be stopped when the application finishes.
|
|
12
|
+
|
|
13
|
+
This sample will:
|
|
14
|
+
1. Create the sample database by calling `rake db:migrate`. This will execute the migrations in the folder `db/migrate`.
|
|
15
|
+
2. Fill the sample database with some data by calling `rake db:seed`. This will execute the script in `db/seeds.rb`.
|
|
16
|
+
The seed script fills the database with 10 random singers and 30 random albums.
|
|
17
|
+
3. Run the `application.rb` file. This application will:
|
|
18
|
+
1. List all known singers and albums.
|
|
19
|
+
2. Select a random singer and update the name of this singer.
|
|
20
|
+
3. Select all singers whose last names start with an 'A'.
|
|
21
|
+
|
|
22
|
+
Run the application with the command
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bundle exec rake run
|
|
26
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require_relative "../config/environment"
|
|
8
|
+
require "sinatra/activerecord/rake"
|
|
9
|
+
|
|
10
|
+
desc "Runs a simple ActiveRecord tutorial application on a Spanner emulator."
|
|
11
|
+
task :run do
|
|
12
|
+
Dir.chdir("..") { sh "bundle exec rake run[quickstart]" }
|
|
13
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Copyright 2021 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
require "io/console"
|
|
8
|
+
require_relative "../config/environment"
|
|
9
|
+
require_relative "models/singer"
|
|
10
|
+
require_relative "models/album"
|
|
11
|
+
|
|
12
|
+
class Application
|
|
13
|
+
def self.run # rubocop:disable Metrics/AbcSize
|
|
14
|
+
# Fetch all singers and albums from the database. The database has been pre-filled by the `db/seeds.rb` script.
|
|
15
|
+
puts "Known singers and their albums:"
|
|
16
|
+
puts ""
|
|
17
|
+
Singer.all.each do |singer|
|
|
18
|
+
puts "#{singer.first_name} #{singer.last_name}"
|
|
19
|
+
singer.albums.each do |album|
|
|
20
|
+
puts " #{album.title}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Select a random singer and update the name of this singer.
|
|
25
|
+
puts ""
|
|
26
|
+
singer = Singer.all.sample
|
|
27
|
+
puts "Current name of singer #{singer.id} is '#{singer.first_name} #{singer.last_name}'"
|
|
28
|
+
puts "Updating name to 'Dave Anderson'"
|
|
29
|
+
singer.first_name = "Dave"
|
|
30
|
+
singer.last_name = "Anderson"
|
|
31
|
+
singer.save!
|
|
32
|
+
singer.reload
|
|
33
|
+
puts "New name of singer #{singer.id}: #{singer.first_name} #{singer.last_name}"
|
|
34
|
+
|
|
35
|
+
# Select all singers whose last name start with 'A'. This should include at least the singer that was updated
|
|
36
|
+
# in the previous step, but probably also a number of other singers.
|
|
37
|
+
puts ""
|
|
38
|
+
puts "Getting all singers with a last name that starts with 'A'"
|
|
39
|
+
|
|
40
|
+
last_name = Singer.arel_table["last_name"]
|
|
41
|
+
Singer.where(last_name.matches("A%")).each do |s|
|
|
42
|
+
puts "#{s.first_name} #{s.last_name}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
puts ""
|
|
46
|
+
puts "Press any key to end the application"
|
|
47
|
+
STDIN.getch
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Application.run
|