activerecord-spanner-adapter 0.3.0 → 0.5.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.
Files changed (264) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/.github/sync-repo-settings.yaml +16 -0
  4. data/.github/workflows/acceptance-tests-on-emulator.yaml +45 -0
  5. data/.github/workflows/acceptance-tests-on-production.yaml +36 -0
  6. data/.github/workflows/ci.yaml +33 -0
  7. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +52 -0
  8. data/.github/workflows/nightly-acceptance-tests-on-production.yaml +35 -0
  9. data/.github/workflows/nightly-unit-tests.yaml +40 -0
  10. data/.github/workflows/release-please-label.yml +25 -0
  11. data/.github/workflows/release-please.yml +39 -0
  12. data/.github/workflows/rubocop.yaml +31 -0
  13. data/.gitignore +67 -5
  14. data/.kokoro/populate-secrets.sh +77 -0
  15. data/.kokoro/release.cfg +33 -0
  16. data/.kokoro/release.sh +15 -0
  17. data/.kokoro/trampoline_v2.sh +489 -0
  18. data/.rubocop.yml +46 -0
  19. data/.toys/release.rb +18 -0
  20. data/.trampolinerc +48 -0
  21. data/.yardopts +11 -0
  22. data/CHANGELOG.md +26 -0
  23. data/CODE_OF_CONDUCT.md +40 -0
  24. data/CONTRIBUTING.md +79 -0
  25. data/Gemfile +9 -4
  26. data/LICENSE +6 -6
  27. data/README.md +67 -30
  28. data/Rakefile +79 -3
  29. data/SECURITY.md +7 -0
  30. data/acceptance/cases/associations/has_many_associations_test.rb +119 -0
  31. data/acceptance/cases/associations/has_many_through_associations_test.rb +63 -0
  32. data/acceptance/cases/associations/has_one_associations_test.rb +79 -0
  33. data/acceptance/cases/associations/has_one_through_associations_test.rb +98 -0
  34. data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +211 -0
  35. data/acceptance/cases/migration/change_schema_test.rb +433 -0
  36. data/acceptance/cases/migration/change_table_test.rb +115 -0
  37. data/acceptance/cases/migration/column_attributes_test.rb +122 -0
  38. data/acceptance/cases/migration/column_positioning_test.rb +48 -0
  39. data/acceptance/cases/migration/columns_test.rb +201 -0
  40. data/acceptance/cases/migration/command_recorder_test.rb +406 -0
  41. data/acceptance/cases/migration/create_join_table_test.rb +216 -0
  42. data/acceptance/cases/migration/ddl_batching_test.rb +80 -0
  43. data/acceptance/cases/migration/foreign_key_test.rb +297 -0
  44. data/acceptance/cases/migration/index_test.rb +211 -0
  45. data/acceptance/cases/migration/references_foreign_key_test.rb +259 -0
  46. data/acceptance/cases/migration/references_index_test.rb +135 -0
  47. data/acceptance/cases/migration/references_statements_test.rb +166 -0
  48. data/acceptance/cases/migration/rename_column_test.rb +96 -0
  49. data/acceptance/cases/models/calculation_query_test.rb +128 -0
  50. data/acceptance/cases/models/generated_column_test.rb +126 -0
  51. data/acceptance/cases/models/mutation_test.rb +122 -0
  52. data/acceptance/cases/models/query_test.rb +147 -0
  53. data/acceptance/cases/sessions/session_not_found_test.rb +121 -0
  54. data/acceptance/cases/transactions/optimistic_locking_test.rb +141 -0
  55. data/acceptance/cases/transactions/read_only_transactions_test.rb +67 -0
  56. data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
  57. data/acceptance/cases/type/all_types_test.rb +152 -0
  58. data/acceptance/cases/type/binary_test.rb +59 -0
  59. data/acceptance/cases/type/boolean_test.rb +31 -0
  60. data/acceptance/cases/type/date_test.rb +32 -0
  61. data/acceptance/cases/type/date_time_test.rb +30 -0
  62. data/acceptance/cases/type/float_test.rb +27 -0
  63. data/acceptance/cases/type/integer_test.rb +44 -0
  64. data/acceptance/cases/type/numeric_test.rb +27 -0
  65. data/acceptance/cases/type/string_test.rb +79 -0
  66. data/acceptance/cases/type/text_test.rb +30 -0
  67. data/acceptance/cases/type/time_test.rb +87 -0
  68. data/acceptance/models/account.rb +13 -0
  69. data/acceptance/models/address.rb +9 -0
  70. data/acceptance/models/album.rb +12 -0
  71. data/acceptance/models/all_types.rb +8 -0
  72. data/acceptance/models/author.rb +11 -0
  73. data/acceptance/models/club.rb +12 -0
  74. data/acceptance/models/comment.rb +9 -0
  75. data/acceptance/models/customer.rb +9 -0
  76. data/acceptance/models/department.rb +9 -0
  77. data/acceptance/models/firm.rb +10 -0
  78. data/acceptance/models/member.rb +13 -0
  79. data/acceptance/models/member_type.rb +9 -0
  80. data/acceptance/models/membership.rb +10 -0
  81. data/acceptance/models/organization.rb +9 -0
  82. data/acceptance/models/post.rb +10 -0
  83. data/acceptance/models/singer.rb +10 -0
  84. data/acceptance/models/track.rb +20 -0
  85. data/acceptance/models/transaction.rb +9 -0
  86. data/acceptance/schema/schema.rb +143 -0
  87. data/acceptance/test_helper.rb +260 -0
  88. data/activerecord-spanner-adapter.gemspec +32 -17
  89. data/assets/solidus-db.png +0 -0
  90. data/benchmarks/README.md +17 -0
  91. data/benchmarks/Rakefile +14 -0
  92. data/benchmarks/application.rb +308 -0
  93. data/benchmarks/config/database.yml +8 -0
  94. data/benchmarks/config/environment.rb +12 -0
  95. data/benchmarks/db/migrate/01_create_tables.rb +25 -0
  96. data/benchmarks/db/schema.rb +29 -0
  97. data/benchmarks/models/album.rb +9 -0
  98. data/benchmarks/models/singer.rb +9 -0
  99. data/bin/console +6 -7
  100. data/examples/rails/README.md +262 -0
  101. data/examples/snippets/README.md +29 -0
  102. data/examples/snippets/Rakefile +57 -0
  103. data/examples/snippets/array-data-type/README.md +45 -0
  104. data/examples/snippets/array-data-type/Rakefile +13 -0
  105. data/examples/snippets/array-data-type/application.rb +45 -0
  106. data/examples/snippets/array-data-type/config/database.yml +8 -0
  107. data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
  108. data/examples/snippets/array-data-type/db/schema.rb +26 -0
  109. data/examples/snippets/array-data-type/db/seeds.rb +5 -0
  110. data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
  111. data/examples/snippets/bin/create_emulator_instance.rb +18 -0
  112. data/examples/snippets/bulk-insert/README.md +21 -0
  113. data/examples/snippets/bulk-insert/Rakefile +13 -0
  114. data/examples/snippets/bulk-insert/application.rb +64 -0
  115. data/examples/snippets/bulk-insert/config/database.yml +8 -0
  116. data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
  117. data/examples/snippets/bulk-insert/db/schema.rb +26 -0
  118. data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
  119. data/examples/snippets/bulk-insert/models/album.rb +9 -0
  120. data/examples/snippets/bulk-insert/models/singer.rb +9 -0
  121. data/examples/snippets/commit-timestamp/README.md +18 -0
  122. data/examples/snippets/commit-timestamp/Rakefile +13 -0
  123. data/examples/snippets/commit-timestamp/application.rb +53 -0
  124. data/examples/snippets/commit-timestamp/config/database.yml +8 -0
  125. data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
  126. data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
  127. data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
  128. data/examples/snippets/commit-timestamp/models/album.rb +9 -0
  129. data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
  130. data/examples/snippets/config/environment.rb +21 -0
  131. data/examples/snippets/create-records/README.md +12 -0
  132. data/examples/snippets/create-records/Rakefile +13 -0
  133. data/examples/snippets/create-records/application.rb +42 -0
  134. data/examples/snippets/create-records/config/database.yml +8 -0
  135. data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
  136. data/examples/snippets/create-records/db/schema.rb +26 -0
  137. data/examples/snippets/create-records/db/seeds.rb +5 -0
  138. data/examples/snippets/create-records/models/album.rb +9 -0
  139. data/examples/snippets/create-records/models/singer.rb +9 -0
  140. data/examples/snippets/date-data-type/README.md +19 -0
  141. data/examples/snippets/date-data-type/Rakefile +13 -0
  142. data/examples/snippets/date-data-type/application.rb +35 -0
  143. data/examples/snippets/date-data-type/config/database.yml +8 -0
  144. data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
  145. data/examples/snippets/date-data-type/db/schema.rb +21 -0
  146. data/examples/snippets/date-data-type/db/seeds.rb +16 -0
  147. data/examples/snippets/date-data-type/models/singer.rb +8 -0
  148. data/examples/snippets/generated-column/README.md +41 -0
  149. data/examples/snippets/generated-column/Rakefile +13 -0
  150. data/examples/snippets/generated-column/application.rb +37 -0
  151. data/examples/snippets/generated-column/config/database.yml +8 -0
  152. data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
  153. data/examples/snippets/generated-column/db/schema.rb +21 -0
  154. data/examples/snippets/generated-column/db/seeds.rb +18 -0
  155. data/examples/snippets/generated-column/models/singer.rb +8 -0
  156. data/examples/snippets/interleaved-tables/README.md +152 -0
  157. data/examples/snippets/interleaved-tables/Rakefile +13 -0
  158. data/examples/snippets/interleaved-tables/application.rb +109 -0
  159. data/examples/snippets/interleaved-tables/config/database.yml +8 -0
  160. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +44 -0
  161. data/examples/snippets/interleaved-tables/db/schema.rb +32 -0
  162. data/examples/snippets/interleaved-tables/db/seeds.rb +40 -0
  163. data/examples/snippets/interleaved-tables/models/album.rb +15 -0
  164. data/examples/snippets/interleaved-tables/models/singer.rb +20 -0
  165. data/examples/snippets/interleaved-tables/models/track.rb +25 -0
  166. data/examples/snippets/migrations/README.md +43 -0
  167. data/examples/snippets/migrations/Rakefile +13 -0
  168. data/examples/snippets/migrations/application.rb +26 -0
  169. data/examples/snippets/migrations/config/database.yml +8 -0
  170. data/examples/snippets/migrations/db/migrate/01_create_tables.rb +28 -0
  171. data/examples/snippets/migrations/db/schema.rb +33 -0
  172. data/examples/snippets/migrations/db/seeds.rb +5 -0
  173. data/examples/snippets/migrations/models/album.rb +10 -0
  174. data/examples/snippets/migrations/models/singer.rb +10 -0
  175. data/examples/snippets/migrations/models/track.rb +9 -0
  176. data/examples/snippets/mutations/README.md +34 -0
  177. data/examples/snippets/mutations/Rakefile +13 -0
  178. data/examples/snippets/mutations/application.rb +47 -0
  179. data/examples/snippets/mutations/config/database.yml +8 -0
  180. data/examples/snippets/mutations/db/migrate/01_create_tables.rb +22 -0
  181. data/examples/snippets/mutations/db/schema.rb +27 -0
  182. data/examples/snippets/mutations/db/seeds.rb +25 -0
  183. data/examples/snippets/mutations/models/album.rb +9 -0
  184. data/examples/snippets/mutations/models/singer.rb +9 -0
  185. data/examples/snippets/optimistic-locking/README.md +12 -0
  186. data/examples/snippets/optimistic-locking/Rakefile +13 -0
  187. data/examples/snippets/optimistic-locking/application.rb +48 -0
  188. data/examples/snippets/optimistic-locking/config/database.yml +8 -0
  189. data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +26 -0
  190. data/examples/snippets/optimistic-locking/db/schema.rb +29 -0
  191. data/examples/snippets/optimistic-locking/db/seeds.rb +25 -0
  192. data/examples/snippets/optimistic-locking/models/album.rb +9 -0
  193. data/examples/snippets/optimistic-locking/models/singer.rb +9 -0
  194. data/examples/snippets/quickstart/README.md +26 -0
  195. data/examples/snippets/quickstart/Rakefile +13 -0
  196. data/examples/snippets/quickstart/application.rb +51 -0
  197. data/examples/snippets/quickstart/config/database.yml +8 -0
  198. data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
  199. data/examples/snippets/quickstart/db/schema.rb +26 -0
  200. data/examples/snippets/quickstart/db/seeds.rb +24 -0
  201. data/examples/snippets/quickstart/models/album.rb +9 -0
  202. data/examples/snippets/quickstart/models/singer.rb +9 -0
  203. data/examples/snippets/read-only-transactions/README.md +13 -0
  204. data/examples/snippets/read-only-transactions/Rakefile +13 -0
  205. data/examples/snippets/read-only-transactions/application.rb +49 -0
  206. data/examples/snippets/read-only-transactions/config/database.yml +8 -0
  207. data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
  208. data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
  209. data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
  210. data/examples/snippets/read-only-transactions/models/album.rb +9 -0
  211. data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
  212. data/examples/snippets/read-write-transactions/README.md +12 -0
  213. data/examples/snippets/read-write-transactions/Rakefile +13 -0
  214. data/examples/snippets/read-write-transactions/application.rb +39 -0
  215. data/examples/snippets/read-write-transactions/config/database.yml +8 -0
  216. data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
  217. data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
  218. data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
  219. data/examples/snippets/read-write-transactions/models/album.rb +9 -0
  220. data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
  221. data/examples/snippets/timestamp-data-type/README.md +17 -0
  222. data/examples/snippets/timestamp-data-type/Rakefile +13 -0
  223. data/examples/snippets/timestamp-data-type/application.rb +42 -0
  224. data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
  225. data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
  226. data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
  227. data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
  228. data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
  229. data/examples/solidus/README.md +172 -0
  230. data/lib/active_record/connection_adapters/spanner/database_statements.rb +224 -269
  231. data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
  232. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
  233. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
  234. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
  235. data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
  236. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
  237. data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
  238. data/lib/active_record/connection_adapters/spanner_adapter.rb +182 -78
  239. data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
  240. data/lib/active_record/type/spanner/array.rb +32 -0
  241. data/lib/active_record/type/spanner/bytes.rb +26 -0
  242. data/lib/active_record/type/spanner/spanner_active_record_converter.rb +32 -0
  243. data/lib/active_record/type/spanner/time.rb +37 -0
  244. data/lib/activerecord-spanner-adapter.rb +23 -0
  245. data/lib/activerecord_spanner_adapter/base.rb +217 -0
  246. data/lib/activerecord_spanner_adapter/connection.rb +324 -0
  247. data/lib/activerecord_spanner_adapter/errors.rb +13 -0
  248. data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
  249. data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
  250. data/lib/activerecord_spanner_adapter/index.rb +80 -0
  251. data/lib/activerecord_spanner_adapter/information_schema.rb +261 -0
  252. data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
  253. data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
  254. data/lib/activerecord_spanner_adapter/table.rb +61 -0
  255. data/lib/activerecord_spanner_adapter/transaction.rb +113 -0
  256. data/lib/activerecord_spanner_adapter/version.rb +9 -0
  257. data/lib/arel/visitors/spanner.rb +35 -0
  258. data/lib/spanner_client_ext.rb +82 -0
  259. data/renovate.json +5 -0
  260. metadata +387 -34
  261. data/.travis.yml +0 -5
  262. data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
  263. data/lib/active_record/connection_adapters/spanner.rb +0 -10
  264. data/lib/activerecord-spanner-adapter/version.rb +0 -3
@@ -1,27 +1,42 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
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 = ["Yuki Yugui Sonoda"]
10
- spec.email = ["yuki.sonoda@supership.jp"]
8
+ spec.authors = ["Google LLC"]
9
+ spec.email = ["cloud-spanner-developers@googlegroups.com"]
11
10
 
12
- spec.summary = %q{Adapts Google Cloud Spanner to ActiveRecord}
13
- spec.description = %q{Connection Adapter of Google Cloud Spanner to ActiveRecord O/R mapper library}
14
- spec.homepage = "https://github.com/supership-jp/activerecord-spanner-adapter"
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
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
- f.match(%r{^(test|spec|features)/})
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.add_dependency 'activerecord', "~> 5.0"
22
- spec.add_dependency 'google-cloud-spanner', "~> 0.23"
23
- spec.add_dependency 'google-gax', '~> 0.8'
24
- spec.add_development_dependency "bundler", "~> 1.14"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
- spec.add_development_dependency "rspec", "~> 3.6.0"
25
+ spec.required_ruby_version = ">= 2.5"
26
+
27
+ spec.add_dependency "google-cloud-spanner", "~> 2.4"
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
+ ```
@@ -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,8 @@
1
+ development:
2
+ adapter: spanner
3
+ project: <benchmark-project>
4
+ instance: <benchmark-instance>
5
+ database: <benchmark-database>
6
+ credentials: <benchmark-credentials>
7
+ pool: 400
8
+ checkout_timeout: 60000
@@ -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
@@ -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
+ 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, null: false, limit: 200
13
+ t.string :full_name, null: false, limit: 300, as: "COALESCE(first_name || ' ', '') || last_name", stored: true
14
+ t.date :birth_date
15
+ t.binary :picture
16
+ end
17
+
18
+ create_table :albums do |t|
19
+ t.string :title
20
+ t.date :release_date
21
+ t.references :singer, index: false
22
+ end
23
+ end
24
+ end
25
+ end