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,261 @@
|
|
|
1
|
+
# Copyright 2020 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
|
+
gem "minitest"
|
|
8
|
+
require "minitest/autorun"
|
|
9
|
+
require "minitest/focus"
|
|
10
|
+
require "minitest/rg"
|
|
11
|
+
require "active_support"
|
|
12
|
+
require "google/cloud/spanner"
|
|
13
|
+
require "active_record"
|
|
14
|
+
require "active_support/testing/stream"
|
|
15
|
+
require "activerecord-spanner-adapter"
|
|
16
|
+
require "active_record/connection_adapters/spanner_adapter"
|
|
17
|
+
require "securerandom"
|
|
18
|
+
|
|
19
|
+
# rubocop:disable Style/GlobalVars
|
|
20
|
+
|
|
21
|
+
$spanner_test_database = "ar-test-#{SecureRandom.hex 4}"
|
|
22
|
+
|
|
23
|
+
def connector_config
|
|
24
|
+
{
|
|
25
|
+
"adapter" => "spanner",
|
|
26
|
+
"emulator_host" => ENV["SPANNER_EMULATOR_HOST"],
|
|
27
|
+
"project" => ENV["SPANNER_TEST_PROJECT"],
|
|
28
|
+
"instance" => ENV["SPANNER_TEST_INSTANCE"],
|
|
29
|
+
"credentials" => ENV["SPANNER_TEST_KEYFILE"],
|
|
30
|
+
"database" => $spanner_test_database
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def spanner
|
|
35
|
+
$spanner ||= Google::Cloud::Spanner.new(
|
|
36
|
+
project_id: ENV["SPANNER_TEST_PROJECT"],
|
|
37
|
+
credentials: ENV["SPANNER_TEST_KEYFILE"]
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def spanner_instance
|
|
42
|
+
unless spanner.instance ENV["SPANNER_TEST_INSTANCE"]
|
|
43
|
+
config = ENV["SPANNER_EMULATOR_HOST"] ? "emulator-config" : "regional-us-central1"
|
|
44
|
+
puts "Creating test instance #{ENV["SPANNER_TEST_INSTANCE"]} with config #{config}"
|
|
45
|
+
job = spanner.create_instance ENV["SPANNER_TEST_INSTANCE"],
|
|
46
|
+
name: "ActiveRecord Test Instance",
|
|
47
|
+
config: config,
|
|
48
|
+
nodes: 1
|
|
49
|
+
job.wait_until_done!
|
|
50
|
+
$owned_test_instance = true
|
|
51
|
+
end
|
|
52
|
+
$spanner_instance ||= spanner.instance ENV["SPANNER_TEST_INSTANCE"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def create_test_database
|
|
56
|
+
job = spanner_instance.create_database $spanner_test_database
|
|
57
|
+
job.wait_until_done!
|
|
58
|
+
if job.error?
|
|
59
|
+
raise "Error in creating database. Error code#{job.error.message}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
puts "'#{$spanner_test_database}' test db created."
|
|
63
|
+
|
|
64
|
+
puts "Loading test schema..."
|
|
65
|
+
ActiveRecord::Base.establish_connection connector_config
|
|
66
|
+
require_relative "schema/schema"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def drop_test_database
|
|
70
|
+
ActiveRecord::Base.connection_pool.disconnect!
|
|
71
|
+
spanner_instance&.delete if $owned_test_instance
|
|
72
|
+
spanner_instance.database($spanner_test_database)&.drop unless $owned_test_instance
|
|
73
|
+
|
|
74
|
+
puts "Test instance #{spanner_instance} deleted" if $owned_test_instance
|
|
75
|
+
puts "#{$spanner_test_database} database deleted" unless $owned_test_instance
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def current_adapter? *names
|
|
79
|
+
names.include? :SpannerAdapter
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def load_test_schema
|
|
83
|
+
ActiveRecord::Base.establish_connection connector_config
|
|
84
|
+
|
|
85
|
+
require_relative "schema/schema"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
module SpannerAdapter
|
|
89
|
+
class TestCase < ActiveSupport::TestCase
|
|
90
|
+
def assert_column(model, column_name, msg = nil)
|
|
91
|
+
assert has_column?(model, column_name), msg
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def assert_no_column(model, column_name, msg = nil)
|
|
95
|
+
assert_not has_column?(model, column_name), msg
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def has_column?(model, column_name)
|
|
99
|
+
model.reset_column_information
|
|
100
|
+
model.column_names.include?(column_name.to_s)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def capture_sql
|
|
104
|
+
ActiveRecord::Base.connection.materialize_transactions
|
|
105
|
+
SQLCounter.clear_log
|
|
106
|
+
yield
|
|
107
|
+
SQLCounter.log.dup
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def assert_queries(num = 1, options = {})
|
|
111
|
+
ignore_none = options.fetch(:ignore_none) { num == :any }
|
|
112
|
+
ActiveRecord::Base.connection.materialize_transactions
|
|
113
|
+
SQLCounter.clear_log
|
|
114
|
+
x = yield
|
|
115
|
+
the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
|
|
116
|
+
if num == :any
|
|
117
|
+
assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed."
|
|
118
|
+
else
|
|
119
|
+
mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}"
|
|
120
|
+
assert_equal num, the_log.size, mesg
|
|
121
|
+
end
|
|
122
|
+
x
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def assert_no_queries(options = {}, &block)
|
|
126
|
+
options.reverse_merge! ignore_none: true
|
|
127
|
+
assert_queries(0, options, &block)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
module Migration
|
|
132
|
+
module TestHelper
|
|
133
|
+
attr_accessor :connection
|
|
134
|
+
|
|
135
|
+
CONNECTION_METHODS = %w[
|
|
136
|
+
add_column remove_column rename_column add_index change_column
|
|
137
|
+
rename_table column_exists? index_exists?
|
|
138
|
+
add_reference add_belongs_to remove_reference remove_references
|
|
139
|
+
remove_belongs_to change_column_default
|
|
140
|
+
].freeze
|
|
141
|
+
|
|
142
|
+
class TestModel < ActiveRecord::Base
|
|
143
|
+
self.table_name = :test_models
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def setup
|
|
147
|
+
ActiveRecord::Base.establish_connection connector_config
|
|
148
|
+
@connection = ActiveRecord::Base.connection
|
|
149
|
+
|
|
150
|
+
unless @skip_test_table_create
|
|
151
|
+
connection.create_table :test_models do |t|
|
|
152
|
+
t.timestamps null: true
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
TestModel.reset_column_information
|
|
156
|
+
end
|
|
157
|
+
super
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def skip_test_table_create!
|
|
161
|
+
@skip_test_table_create = true
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def teardown
|
|
165
|
+
TestModel.reset_table_name
|
|
166
|
+
|
|
167
|
+
unless @skip_test_table_create
|
|
168
|
+
connection.drop_table :test_models, if_exists: true
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
super
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def generate_id
|
|
175
|
+
connection.next_sequence_value nil
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
delegate *CONNECTION_METHODS, to: :connection
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
module Types
|
|
183
|
+
module TestHelper
|
|
184
|
+
attr_accessor :connection
|
|
185
|
+
|
|
186
|
+
class TestTypeModel < ActiveRecord::Base
|
|
187
|
+
self.table_name = :test_types
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def setup
|
|
191
|
+
super
|
|
192
|
+
|
|
193
|
+
ActiveRecord::Base.establish_connection connector_config
|
|
194
|
+
@connection = ActiveRecord::Base.connection
|
|
195
|
+
|
|
196
|
+
return if connection.table_exists? :test_types
|
|
197
|
+
|
|
198
|
+
connection.create_table :test_types do |t|
|
|
199
|
+
t.string :name, limit: 255
|
|
200
|
+
t.string :description
|
|
201
|
+
t.text :bio
|
|
202
|
+
t.integer :length
|
|
203
|
+
t.float :weight
|
|
204
|
+
t.numeric :price
|
|
205
|
+
t.boolean :active
|
|
206
|
+
t.binary :file
|
|
207
|
+
t.binary :data, limit: 255
|
|
208
|
+
t.date :start_date
|
|
209
|
+
t.datetime :start_datetime
|
|
210
|
+
t.time :start_time
|
|
211
|
+
t.json :details unless ENV["SPANNER_EMULATOR_HOST"]
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def teardown
|
|
216
|
+
super
|
|
217
|
+
TestTypeModel.delete_all
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
module Associations
|
|
223
|
+
module TestHelper
|
|
224
|
+
def setup
|
|
225
|
+
ActiveRecord::Base.establish_connection connector_config
|
|
226
|
+
@connection = ActiveRecord::Base.connection
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
class SQLCounter
|
|
232
|
+
class << self
|
|
233
|
+
attr_accessor :ignored_sql, :log, :log_all
|
|
234
|
+
|
|
235
|
+
def clear_log
|
|
236
|
+
self.log = []
|
|
237
|
+
self.log_all = []
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
clear_log
|
|
242
|
+
|
|
243
|
+
def call(name, start, finish, message_id, values)
|
|
244
|
+
return if values[:cached]
|
|
245
|
+
|
|
246
|
+
sql = values[:sql]
|
|
247
|
+
self.class.log_all << sql
|
|
248
|
+
self.class.log << sql unless ["SCHEMA", "TRANSACTION"].include? values[:name]
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
ActiveSupport::Notifications.subscribe("sql.active_record", SQLCounter.new)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
Minitest.after_run do
|
|
256
|
+
drop_test_database
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
create_test_database
|
|
260
|
+
|
|
261
|
+
# rubocop:enable Style/GlobalVars
|
|
@@ -1,27 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
lib
|
|
3
|
-
|
|
4
|
-
require 'activerecord-spanner-adapter/version'
|
|
1
|
+
lib = File.expand_path "lib", __dir__
|
|
2
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
|
|
3
|
+
require "activerecord_spanner_adapter/version"
|
|
5
4
|
|
|
6
5
|
Gem::Specification.new do |spec|
|
|
7
6
|
spec.name = "activerecord-spanner-adapter"
|
|
8
7
|
spec.version = ActiveRecordSpannerAdapter::VERSION
|
|
9
|
-
spec.authors = ["
|
|
10
|
-
spec.email = ["
|
|
8
|
+
spec.authors = ["Google LLC"]
|
|
9
|
+
spec.email = ["cloud-spanner-developers@googlegroups.com"]
|
|
11
10
|
|
|
12
|
-
spec.summary = %q{
|
|
13
|
-
spec.description = %q{
|
|
14
|
-
spec.homepage = "https://github.com/
|
|
11
|
+
spec.summary = %q{Rails ActiveRecord connector for Google Spanner Database}
|
|
12
|
+
spec.description = %q{Rails ActiveRecord connector for Google Spanner Database}
|
|
13
|
+
spec.homepage = "https://github.com/googleapis/ruby-spanner-activerecord"
|
|
14
|
+
spec.license = "MIT"
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
18
|
+
spec.files = Dir.chdir File.expand_path(__dir__) do
|
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match %r{^(test|spec|features)/} }
|
|
18
20
|
end
|
|
21
|
+
spec.bindir = "exe"
|
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename f }
|
|
19
23
|
spec.require_paths = ["lib"]
|
|
20
24
|
|
|
21
|
-
spec.
|
|
22
|
-
|
|
23
|
-
spec.add_dependency
|
|
24
|
-
spec.
|
|
25
|
-
|
|
26
|
-
spec.add_development_dependency "
|
|
25
|
+
spec.required_ruby_version = ">= 2.5"
|
|
26
|
+
|
|
27
|
+
spec.add_dependency "google-cloud-spanner", "~> 2.10"
|
|
28
|
+
spec.add_runtime_dependency "activerecord", "~> 6.1.4"
|
|
29
|
+
|
|
30
|
+
spec.add_development_dependency "autotest-suffix", "~> 1.1"
|
|
31
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
|
32
|
+
spec.add_development_dependency "google-style", "~> 1.24.0"
|
|
33
|
+
spec.add_development_dependency "minitest", "~> 5.10"
|
|
34
|
+
spec.add_development_dependency "minitest-autotest", "~> 1.0"
|
|
35
|
+
spec.add_development_dependency "minitest-focus", "~> 1.1"
|
|
36
|
+
spec.add_development_dependency "minitest-rg", "~> 5.2"
|
|
37
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
38
|
+
spec.add_development_dependency "redcarpet", "~> 3.0"
|
|
39
|
+
spec.add_development_dependency "simplecov", "~> 0.9"
|
|
40
|
+
spec.add_development_dependency "yard", "~> 0.9"
|
|
41
|
+
spec.add_development_dependency "yard-doctest", "~> 0.1.13"
|
|
27
42
|
end
|
|
Binary file
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Benchmarks
|
|
2
|
+
|
|
3
|
+
Benchmarks the Spanner ActiveRecord adapter using a small set of standardized use cases. The benchmarks consists of two
|
|
4
|
+
separate runs:
|
|
5
|
+
* Execute each use case separately and measure the time needed for each use case.
|
|
6
|
+
* Batch all use cases together and execute multiple batches in parallel, measuring the time needed to finish all
|
|
7
|
+
batches. The number of batches varies between 1 and 400. The session pool is configured to contain at most 400
|
|
8
|
+
connections.
|
|
9
|
+
|
|
10
|
+
Change the configuration in the file `config/database.yml` before running the benchmarks. The instance and database in
|
|
11
|
+
the configuration must exist. The tables that are needed will automatically be created by the benchmark script.
|
|
12
|
+
|
|
13
|
+
Run the benchmark with the command
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle exec rake run
|
|
17
|
+
```
|
data/benchmarks/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
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 benchmarks against a Cloud Spanner database."
|
|
11
|
+
task :run do |_t, _args|
|
|
12
|
+
sh "rake db:migrate"
|
|
13
|
+
sh "ruby application.rb"
|
|
14
|
+
end
|
|
@@ -0,0 +1,308 @@
|
|
|
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 "benchmark"
|
|
8
|
+
require "io/console"
|
|
9
|
+
require "securerandom"
|
|
10
|
+
require_relative "config/environment"
|
|
11
|
+
require_relative "models/singer"
|
|
12
|
+
require_relative "models/album"
|
|
13
|
+
|
|
14
|
+
class Application
|
|
15
|
+
def self.run # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
16
|
+
ActiveRecord::Base.logger.level = Logger::WARN
|
|
17
|
+
config = ActiveRecord::Base.connection_config
|
|
18
|
+
spanner = Google::Cloud::Spanner.new project: config[:project], credentials: config[:credentials]
|
|
19
|
+
spanner_client = spanner.client config[:instance], config[:database], pool: { max: config[:pool], fail: false }
|
|
20
|
+
|
|
21
|
+
[nil, spanner_client].each do |client| # rubocop:disable Metrics/BlockLength
|
|
22
|
+
puts ""
|
|
23
|
+
puts ""
|
|
24
|
+
puts "Benchmarks for #{client ? 'Spanner client' : 'ActiveRecord'}"
|
|
25
|
+
Album.delete_all
|
|
26
|
+
Singer.delete_all
|
|
27
|
+
|
|
28
|
+
# Seed the database with 10,000 random singers.
|
|
29
|
+
# Having a relatively large number of records in the database prevents the parallel test cases from using the same
|
|
30
|
+
# records, which would cause many of the transactions to be aborted and retried all the time.
|
|
31
|
+
singer = nil
|
|
32
|
+
10.times do
|
|
33
|
+
singer = create_singers 1000, client
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
execute_individual_benchmarks singer, client
|
|
37
|
+
|
|
38
|
+
Benchmark.bm 75 do |bm|
|
|
39
|
+
[1, 5, 10, 25, 50, 100, 200, 400].each do |parallel_benchmarks|
|
|
40
|
+
bm.report "Total execution time (#{parallel_benchmarks}):" do
|
|
41
|
+
threads = []
|
|
42
|
+
parallel_benchmarks.times do
|
|
43
|
+
threads << Thread.new do
|
|
44
|
+
benchmark_select_one_singer singer, client
|
|
45
|
+
benchmark_select_and_update_using_mutation 1, client
|
|
46
|
+
benchmark_select_and_update_using_dml 1, client
|
|
47
|
+
benchmark_create_and_reload client
|
|
48
|
+
benchmark_create_albums_using_mutations 1, client
|
|
49
|
+
benchmark_create_albums_using_dml 1, client
|
|
50
|
+
benchmark_select_100_singers client
|
|
51
|
+
benchmark_select_100_singers_in_read_only_transaction client
|
|
52
|
+
benchmark_select_100_singers_in_read_write_transaction client
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
threads.each(&:join)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
spanner_client.close
|
|
62
|
+
ActiveRecord::Base.connection_pool.disconnect
|
|
63
|
+
|
|
64
|
+
puts ""
|
|
65
|
+
puts "Press any key to end the application"
|
|
66
|
+
STDIN.getch
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.execute_individual_benchmarks singer, client
|
|
70
|
+
puts ""
|
|
71
|
+
Benchmark.bm 75 do |bm| # rubocop:disable Metrics/BlockLength
|
|
72
|
+
bm.report "Select one row:" do
|
|
73
|
+
benchmark_select_one_singer singer, client
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
bm.report "Save one row with fetch after:" do
|
|
77
|
+
benchmark_create_and_reload client
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
bm.report "Select and update 1 row in transaction using mutation:" do
|
|
81
|
+
benchmark_select_and_update_using_mutation 1, client
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
bm.report "Select and update 1 row in transaction using DML:" do
|
|
85
|
+
benchmark_select_and_update_using_dml 1, client
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
bm.report "Select and update 25 rows in transaction using mutation:" do
|
|
89
|
+
benchmark_select_and_update_using_mutation 25, client
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
bm.report "Select and update 25 rows in transaction using DML:" do
|
|
93
|
+
benchmark_select_and_update_using_dml 25, client
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
bm.report "Create 100 albums using mutations:" do
|
|
97
|
+
benchmark_create_albums_using_mutations 100, client
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
bm.report "Create 100 albums using DML:" do
|
|
101
|
+
benchmark_create_albums_using_dml 100, client
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
bm.report "Select and iterate over 100 singers:" do
|
|
105
|
+
benchmark_select_100_singers client
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
bm.report "Select and iterate over 100 singers in a read-only transaction:" do
|
|
109
|
+
benchmark_select_100_singers_in_read_only_transaction client
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
bm.report "Select and iterate over 100 singers in a read/write transaction:" do
|
|
113
|
+
benchmark_select_100_singers_in_read_write_transaction client
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.benchmark_select_one_singer singer, client
|
|
119
|
+
if client
|
|
120
|
+
sql = "SELECT * FROM Singers WHERE id=@id"
|
|
121
|
+
params = { id: singer[:id] }
|
|
122
|
+
param_types = { id: :INT64 }
|
|
123
|
+
client.execute(sql, params: params, types: param_types).rows.each do |row|
|
|
124
|
+
return row
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
Singer.find singer.id
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.benchmark_create_and_reload client
|
|
132
|
+
singer = create_singers 1, client
|
|
133
|
+
if client
|
|
134
|
+
sql = "SELECT * FROM Singers WHERE id=@id"
|
|
135
|
+
params = { id: singer[:id] }
|
|
136
|
+
param_types = { id: :INT64 }
|
|
137
|
+
client.execute(sql, params: params, types: param_types).rows.each do |row|
|
|
138
|
+
return row
|
|
139
|
+
end
|
|
140
|
+
else
|
|
141
|
+
singer.reload
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.benchmark_select_and_update_using_mutation count, client
|
|
146
|
+
benchmark_select_and_update count, :buffered_mutations, client
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def self.benchmark_select_and_update_using_dml count, client
|
|
150
|
+
benchmark_select_and_update count, :serializable, client
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def self.benchmark_select_and_update count, isolation, client
|
|
154
|
+
# Select some random singers OUTSIDE of a transaction to prevent the random select to cause transactions to abort.
|
|
155
|
+
sql = "SELECT id FROM singers TABLESAMPLE RESERVOIR (#{count} ROWS)"
|
|
156
|
+
rows = Singer.connection.select_all(sql).to_a
|
|
157
|
+
if client
|
|
158
|
+
client.transaction do |transaction|
|
|
159
|
+
rows.each do |row|
|
|
160
|
+
singer = transaction.execute("SELECT * FROM singers WHERE id=@id",
|
|
161
|
+
params: { id: row["id"] },
|
|
162
|
+
types: { id: :INT64 }).rows.first
|
|
163
|
+
if isolation == :buffered_mutations
|
|
164
|
+
transaction.update "singers", id: singer[:id], last_name: SecureRandom.uuid
|
|
165
|
+
else
|
|
166
|
+
params = { id: singer[:id], last_name: SecureRandom.uuid }
|
|
167
|
+
param_types = { id: :INT64, last_name: :STRING }
|
|
168
|
+
transaction.execute_update "UPDATE singers SET last_name=@last_name WHERE id=@id",
|
|
169
|
+
params: params,
|
|
170
|
+
types: param_types
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
else
|
|
175
|
+
Singer.transaction isolation: isolation do
|
|
176
|
+
rows.each do |row|
|
|
177
|
+
singer = Singer.find row["id"]
|
|
178
|
+
singer.update last_name: SecureRandom.uuid
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def self.benchmark_create_albums_using_mutations count, client
|
|
185
|
+
create_albums count, :buffered_mutations, client
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def self.benchmark_create_albums_using_dml count, client
|
|
189
|
+
create_albums count, :serializable, client
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def self.benchmark_select_100_singers client
|
|
193
|
+
sql = "SELECT * FROM singers TABLESAMPLE RESERVOIR (100 ROWS)"
|
|
194
|
+
count = 0
|
|
195
|
+
if client
|
|
196
|
+
client.execute(sql).rows.each do |_row|
|
|
197
|
+
count += 1
|
|
198
|
+
end
|
|
199
|
+
else
|
|
200
|
+
Singer.find_by_sql(sql).each do
|
|
201
|
+
count += 1
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def self.benchmark_select_100_singers_in_read_only_transaction client
|
|
207
|
+
sql = "SELECT * FROM singers TABLESAMPLE RESERVOIR (100 ROWS)"
|
|
208
|
+
count = 0
|
|
209
|
+
if client
|
|
210
|
+
client.snapshot do |snapshot|
|
|
211
|
+
snapshot.execute(sql).rows.each do |_row|
|
|
212
|
+
count += 1
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
else
|
|
216
|
+
Singer.transaction isolation: :read_only do
|
|
217
|
+
Singer.find_by_sql(sql).each do
|
|
218
|
+
count += 1
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def self.benchmark_select_100_singers_in_read_write_transaction client
|
|
225
|
+
sql = "SELECT * FROM singers TABLESAMPLE RESERVOIR (100 ROWS)"
|
|
226
|
+
count = 0
|
|
227
|
+
if client
|
|
228
|
+
client.transaction do |transaction|
|
|
229
|
+
transaction.execute(sql).rows.each do |_row|
|
|
230
|
+
count += 1
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
else
|
|
234
|
+
Singer.transaction do
|
|
235
|
+
Singer.find_by_sql(sql).each do
|
|
236
|
+
count += 1
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def self.create_singers count, client
|
|
243
|
+
first_names = %w[Pete Alice John Ethel Trudy Naomi Wendy Ruben Thomas Elly Cora Elise April Libby Alexandra Shania]
|
|
244
|
+
last_names = %w[Wendelson Allison Peterson Johnson Henderson Ericsson Aronson Tennet Courtou Mcdonald Berry Ramirez]
|
|
245
|
+
|
|
246
|
+
last_singer = nil
|
|
247
|
+
if client
|
|
248
|
+
client.commit do |c|
|
|
249
|
+
singers = []
|
|
250
|
+
count.times do
|
|
251
|
+
last_singer = { id: SecureRandom.uuid.gsub("-", "").hex & 0x7FFFFFFFFFFFFFFF,
|
|
252
|
+
first_name: first_names.sample, last_name: last_names.sample,
|
|
253
|
+
birth_date: Date.new(rand(1920..2005), rand(1..12), rand(1..28)),
|
|
254
|
+
picture: StringIO.new(SecureRandom.uuid) }
|
|
255
|
+
singers << last_singer
|
|
256
|
+
end
|
|
257
|
+
c.insert "singers", singers
|
|
258
|
+
end
|
|
259
|
+
else
|
|
260
|
+
Singer.transaction isolation: :buffered_mutations do
|
|
261
|
+
count.times do
|
|
262
|
+
last_singer = Singer.create first_name: first_names.sample, last_name: last_names.sample,
|
|
263
|
+
birth_date: Date.new(rand(1920..2005), rand(1..12), rand(1..28)),
|
|
264
|
+
picture: StringIO.new("some-picture-#{SecureRandom.uuid}")
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
last_singer
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def self.create_albums count, isolation, client
|
|
272
|
+
sql = "SELECT * FROM singers TABLESAMPLE RESERVOIR (1 ROWS)"
|
|
273
|
+
singer = Singer.find_by_sql(sql).first
|
|
274
|
+
if client
|
|
275
|
+
if isolation == :buffered_mutations
|
|
276
|
+
client.commit do |c|
|
|
277
|
+
albums = []
|
|
278
|
+
count.times do
|
|
279
|
+
albums << { id: SecureRandom.uuid.gsub("-", "").hex & 0x7FFFFFFFFFFFFFFF,
|
|
280
|
+
singer_id: singer.id, title: "Some random title",
|
|
281
|
+
release_date: Date.new(2021, 7, 1) }
|
|
282
|
+
end
|
|
283
|
+
c.insert "albums", albums
|
|
284
|
+
end
|
|
285
|
+
else
|
|
286
|
+
client.transaction do |transaction|
|
|
287
|
+
sql = "INSERT INTO albums (id, singer_id, title, release_date) VALUES (@id, @singer, @title, @release_date)"
|
|
288
|
+
transaction.batch_update do |b|
|
|
289
|
+
count.times do
|
|
290
|
+
params = { id: SecureRandom.uuid.gsub("-", "").hex & 0x7FFFFFFFFFFFFFFF, singer: singer.id,
|
|
291
|
+
title: "Some random title", release_date: Date.new(2021, 7, 1) }
|
|
292
|
+
param_types = { id: :INT64, singer: :INT64, title: :STRING, release_date: :DATE }
|
|
293
|
+
b.batch_update sql, params: params, types: param_types
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
else
|
|
299
|
+
Album.transaction isolation: isolation do
|
|
300
|
+
count.times do
|
|
301
|
+
Album.create singer: singer, title: "Some random title", release_date: Date.new(2021, 7, 1)
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
Application.run
|
|
@@ -0,0 +1,12 @@
|
|
|
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 "active_record"
|
|
8
|
+
require "bundler"
|
|
9
|
+
|
|
10
|
+
Dir["../lib/*.rb"].each { |file| require file }
|
|
11
|
+
|
|
12
|
+
Bundler.require
|