identity_cache 1.2.0 → 1.6.3

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +8 -7
  3. data/.gitignore +1 -1
  4. data/.rubocop.yml +0 -1
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +84 -2
  7. data/Gemfile +4 -4
  8. data/Gemfile.lock +118 -0
  9. data/README.md +28 -5
  10. data/Rakefile +98 -6
  11. data/dev.yml +17 -17
  12. data/gemfiles/Gemfile.latest-release +1 -0
  13. data/gemfiles/Gemfile.min-supported +5 -5
  14. data/gemfiles/Gemfile.rails-edge +1 -0
  15. data/identity_cache.gemspec +3 -3
  16. data/lib/identity_cache/belongs_to_caching.rb +1 -1
  17. data/lib/identity_cache/cache_fetcher.rb +12 -3
  18. data/lib/identity_cache/cache_key_loader.rb +1 -1
  19. data/lib/identity_cache/cached/attribute.rb +66 -3
  20. data/lib/identity_cache/cached/attribute_by_multi.rb +94 -9
  21. data/lib/identity_cache/cached/attribute_by_one.rb +4 -40
  22. data/lib/identity_cache/cached/belongs_to.rb +14 -10
  23. data/lib/identity_cache/cached/primary_index.rb +10 -2
  24. data/lib/identity_cache/cached/recursive/association.rb +1 -1
  25. data/lib/identity_cache/cached/reference/has_many.rb +1 -1
  26. data/lib/identity_cache/configuration_dsl.rb +1 -1
  27. data/lib/identity_cache/encoder.rb +1 -0
  28. data/lib/identity_cache/fallback_fetcher.rb +5 -1
  29. data/lib/identity_cache/memoized_cache_proxy.rb +10 -0
  30. data/lib/identity_cache/parent_model_expiration.rb +8 -3
  31. data/lib/identity_cache/query_api.rb +12 -6
  32. data/lib/identity_cache/should_use_cache.rb +10 -0
  33. data/lib/identity_cache/version.rb +1 -1
  34. data/lib/identity_cache/with_primary_index.rb +27 -14
  35. data/lib/identity_cache.rb +131 -21
  36. data/shipit.rubygems.yml +4 -1
  37. metadata +13 -12
  38. data/isogun.yml +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 561965856845fc5d5094581d735584924eda58557edfb3831c39bce2498635aa
4
- data.tar.gz: 33a85428a3b2a298076d81a79b662b742c00a25971bf453f8f57eeab7cba2681
3
+ metadata.gz: 0daa9d36baa2b4c2e08dc8c29ab55274a1dbfab1d0146010720a68bfe9eff20e
4
+ data.tar.gz: '08708fb388587b7def00967250321904ffbfa9c071aca71f6b360ea22ec0ab8f'
5
5
  SHA512:
6
- metadata.gz: 6a8c691d8883acfd69f11724dfff13a3ef4858a2abd2ca658659981f1d08b36dea244100c4030b8ef5d39ac8509e639fea6cd02127e09d157be636fcc2bee77a
7
- data.tar.gz: e357c3a4f02a288a5ddb3290cbd11176937821bb3c9052fb318d9139ac253650f7018a8249b2ebc9f115015a6434baf7cb1b7e76bfb0dbf1e98ee745e3e95ed7
6
+ metadata.gz: 61e4c7010330259878bb07f61a806b2b8d21d3cfbc67d07074cd9dddafd69471c6aa50ebdf880e39b9a0dba7203d0215f1336829842457ea877e3f065ce8e98e
7
+ data.tar.gz: e1d0f10f2a4a3ff678b4b89b60547c342ab6a0ce8f5d1623ebcdb29c227e08ee7da8234c32ed3388b745aa11f4be1989d5a554a521152bdd72ebaa9c92cc9747
@@ -16,14 +16,14 @@ jobs:
16
16
  matrix:
17
17
  entry:
18
18
  - name: 'Minimum supported'
19
- ruby: '2.5'
19
+ ruby: '3.0'
20
20
  gemfile: "Gemfile.min-supported"
21
21
  - name: 'Latest released & run rubocop'
22
- ruby: '3.0'
22
+ ruby: '3.3'
23
23
  gemfile: "Gemfile.latest-release"
24
24
  rubocop: true
25
25
  - name: 'Rails edge'
26
- ruby: '3.0'
26
+ ruby: '3.3'
27
27
  gemfile: "Gemfile.rails-edge"
28
28
  edge: true
29
29
 
@@ -69,25 +69,26 @@ jobs:
69
69
  run: |
70
70
  sudo apt-get update
71
71
  sudo apt-get -y install libmemcached-dev libmysqlclient-dev libpq-dev libsasl2-dev
72
+
72
73
  - uses: actions/checkout@v2
73
74
  - name: Set up Ruby
74
75
  uses: ruby/setup-ruby@v1
75
76
  with:
76
77
  ruby-version: ${{ matrix.entry.ruby }}
77
- - name: Install bundler and gems
78
- run: |
79
- gem install bundler
80
- bundle install --jobs 4 --retry 3
78
+ bundler-cache: true
79
+
81
80
  - name: Test with mysql
82
81
  env:
83
82
  DB: mysql2
84
83
  run: bundle exec rake test
84
+
85
85
  - name: Test with postgres and memcached_store
86
86
  env:
87
87
  DB: postgresql
88
88
  POSTGRES_PASSWORD: postgres
89
89
  ADAPTER: memcached
90
90
  run: bundle exec rake test
91
+
91
92
  - name: Run rubocop
92
93
  if: matrix.entry.rubocop
93
94
  run: bundle exec rubocop
data/.gitignore CHANGED
@@ -3,7 +3,6 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- *.lock
7
6
  InstalledFiles
8
7
  _yardoc
9
8
  coverage
@@ -17,3 +16,4 @@ test/version_tmp
17
16
  tmp
18
17
  .rubocop-http*
19
18
  .byebug_history
19
+ gemfiles/*.lock
data/.rubocop.yml CHANGED
@@ -2,7 +2,6 @@ inherit_gem:
2
2
  rubocop-shopify: rubocop.yml
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.5
6
5
  NewCops: disable
7
6
 
8
7
  Layout/BeginEndAlignment:
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.4
data/CHANGELOG.md CHANGED
@@ -1,18 +1,99 @@
1
1
  # Identity Cache Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 1.6.3
6
+
7
+ - Split the `with_deferred_parent_expiration` and `with_deferred_parent_expiration`. (#578)
8
+
9
+ ## 1.6.2
10
+
11
+ - Support deferred expiry of associations and attributes. Add a rake task to create test database. (#577)
12
+
13
+ ## 1.6.1
14
+
15
+ - Fix deprecation warnings on Active Record 7.2. (#575)
16
+
17
+ ## 1.6.0
18
+
19
+ - Introduce `.with_deferred_parent_expiration`, which takes a block and avoids duplicate parent cache expiry. (#569)
20
+
21
+ ## 1.5.6
22
+
23
+ - Minor performance improvements on association read
24
+
25
+ ## 1.5.5
26
+
27
+ - Minor performance improvements on association read
28
+
29
+
30
+ ## 1.5.4
31
+
32
+ - Make `prefetch_associations` work as expected on associations that have been partially prefetched
33
+
34
+ ## 1.5.3
35
+
36
+ - No longer call `should_use_cache?` on embedded/prefetched associations when reading values
37
+
38
+ ## 1.5.2
39
+
40
+ - Add missing `should_use_cache?` to `fetch_multi` methods.
41
+
42
+ ## 1.5.1
43
+
44
+ - Fix parent cache invalidation for custom foreign key associations
45
+
46
+ ## 1.5.0
47
+
48
+ - Require Active Record 7+
49
+ - Require Ruby 3+
50
+ - Improve compatibility with Active Record 7.1.
51
+
52
+ ## 1.4.1
53
+
54
+ ### Fixes
55
+
56
+ - Fix `fetch_multi_by` bug for queries having a single field with distinct values. (#536)
57
+
58
+ ## 1.4.0
59
+
60
+ ### Features
61
+
62
+ - Add `fetch_multi_by` support for composite-key indexes. (#534)
63
+
64
+ ## 1.3.1
65
+
66
+ ### Fixes
67
+
68
+ - Remove N+1 queries from embedded associations when using `fetch` while `should_use_cache` is false. (#531)
69
+
70
+ ## 1.3.0
71
+
72
+ ### Features
73
+
74
+ - Return meaningful value from `expire_cache` indicating whenever it succeeded or failed in the process. (#523)
75
+
76
+ ### Fixes
77
+
78
+ - Expire parents cache when when calling `expire_cache`. (#523)
79
+ - Avoid creating too many shapes on Ruby 3.2+. (#526)
80
+
3
81
  ## 1.2.0
4
82
 
5
83
  ### Fixes
84
+
6
85
  - Fix mem_cache_store adapter with pool_size (#489)
7
86
  - Fix dalli deprecation warning about requiring 'dalli/cas/client' (#511)
8
87
  - Make transitionary method IdentityCache.with_fetch_read_only_records thread-safe (#503)
9
88
 
10
89
  ### Features
90
+
11
91
  - Add support for fill lock with lock wait to avoid thundering herd problem (#373)
12
92
 
13
93
  ## 1.1.0
14
94
 
15
95
  ### Fixes
96
+
16
97
  - Fix double debug logging of cache hits and misses (#474)
17
98
  - Fix a Rails 6.1 deprecation warning for Rails 7.0 compatibility (#482)
18
99
  - Recursively install parent expiry hooks when expiring parent caches (#476)
@@ -24,10 +105,12 @@
24
105
  - Fix fetch `has_many` embedded association on record after adding to it (#449)
25
106
 
26
107
  ### Features
108
+
27
109
  - Support multiple databases and transactional tests in `IdentityCache.should_use_cache?` (#293)
28
110
  - Add support for the default `MemCacheStore` from `ActiveSupport` (#465)
29
111
 
30
112
  ### Breaking Changes
113
+
31
114
  - Drop ruby 2.4 support, since it is no longer supported upstream (#468)
32
115
 
33
116
  ## 1.0.1
@@ -99,7 +182,7 @@
99
182
  - Remove support for 3.2
100
183
  - Fix N+1 from fetching embedded ids on a cache miss
101
184
  - Raise when trying to cache a through association. Previously it wouldn't be invalidated properly.
102
- - Raise if a class method is called on a scope. Previously the scope was ignored.
185
+ - Raise if a class method is called on a scope. Previously the scope was ignored.
103
186
  - Raise if a class method is called on a subclass of one that included IdentityCache. This never worked properly.
104
187
  - Fix cache_belongs_to on polymorphic assocations.
105
188
  - Fetching a cache_belongs_to association no longer loads the belongs_to association
@@ -160,7 +243,6 @@
160
243
 
161
244
  ## 0.0.5
162
245
 
163
-
164
246
  ## 0.0.4
165
247
 
166
248
  - Fix: only marshal attributes, embedded associations and normalized association IDs
data/Gemfile CHANGED
@@ -3,15 +3,15 @@
3
3
  source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
- gem "rubocop", "~> 1.5"
6
+ gem "rubocop", "~> 1.61.0"
7
7
 
8
8
  gem "rubocop-shopify", "~> 2.9.0", require: false
9
9
 
10
10
  gem "mysql2", "~> 0.5.3", platform: :mri
11
11
  gem "pg", ">= 0.18", "< 2.0", platform: :mri
12
- gem "memcached", "~> 1.8.0", platform: :mri
13
- gem "memcached_store", "~> 1.0.0", platform: :mri
14
- gem "dalli", "~> 2.7.11"
12
+ gem "memcached", github: "Shopify/memcached", branch: "1-0-stable-shopify", platform: :mri
13
+ gem "memcached_store", "~> 2.3.2", platform: :mri
14
+ gem "dalli", "~> 3.2.3"
15
15
  gem "cityhash", "~> 0.6.0", platform: :mri
16
16
 
17
17
  gem "byebug", platform: :mri
data/Gemfile.lock ADDED
@@ -0,0 +1,118 @@
1
+ GIT
2
+ remote: https://github.com/Shopify/memcached.git
3
+ revision: 54a89bae698896a00fd2ceeb142dada6285f73ed
4
+ branch: 1-0-stable-shopify
5
+ specs:
6
+ memcached (1.9.0)
7
+
8
+ PATH
9
+ remote: .
10
+ specs:
11
+ identity_cache (1.6.3)
12
+ activerecord (>= 7.0)
13
+ ar_transaction_changes (~> 1.1)
14
+
15
+ GEM
16
+ remote: https://rubygems.org/
17
+ specs:
18
+ activemodel (7.2.0)
19
+ activesupport (= 7.2.0)
20
+ activerecord (7.2.0)
21
+ activemodel (= 7.2.0)
22
+ activesupport (= 7.2.0)
23
+ timeout (>= 0.4.0)
24
+ activesupport (7.2.0)
25
+ base64
26
+ bigdecimal
27
+ concurrent-ruby (~> 1.0, >= 1.3.1)
28
+ connection_pool (>= 2.2.5)
29
+ drb
30
+ i18n (>= 1.6, < 2)
31
+ logger (>= 1.4.2)
32
+ minitest (>= 5.1)
33
+ securerandom (>= 0.3)
34
+ tzinfo (~> 2.0, >= 2.0.5)
35
+ ar_transaction_changes (1.1.9)
36
+ activerecord (>= 5.2.0)
37
+ ast (2.4.2)
38
+ base64 (0.2.0)
39
+ bigdecimal (3.1.8)
40
+ byebug (11.1.3)
41
+ cityhash (0.6.0)
42
+ concurrent-ruby (1.3.4)
43
+ connection_pool (2.4.1)
44
+ dalli (3.2.3)
45
+ drb (2.2.1)
46
+ i18n (1.14.5)
47
+ concurrent-ruby (~> 1.0)
48
+ json (2.7.1)
49
+ language_server-protocol (3.17.0.3)
50
+ logger (1.6.0)
51
+ memcached_store (2.3.4)
52
+ activesupport (>= 6)
53
+ memcached (~> 1.8)
54
+ minitest (5.25.1)
55
+ mocha (2.1.0)
56
+ ruby2_keywords (>= 0.0.5)
57
+ mysql2 (0.5.6)
58
+ parallel (1.24.0)
59
+ parser (3.3.0.5)
60
+ ast (~> 2.4.1)
61
+ racc
62
+ pg (1.5.6)
63
+ racc (1.7.3)
64
+ rainbow (3.1.1)
65
+ rake (13.1.0)
66
+ regexp_parser (2.9.0)
67
+ rexml (3.3.6)
68
+ strscan
69
+ rubocop (1.61.0)
70
+ json (~> 2.3)
71
+ language_server-protocol (>= 3.17.0)
72
+ parallel (~> 1.10)
73
+ parser (>= 3.3.0.2)
74
+ rainbow (>= 2.2.2, < 4.0)
75
+ regexp_parser (>= 1.8, < 3.0)
76
+ rexml (>= 3.2.5, < 4.0)
77
+ rubocop-ast (>= 1.30.0, < 2.0)
78
+ ruby-progressbar (~> 1.7)
79
+ unicode-display_width (>= 2.4.0, < 3.0)
80
+ rubocop-ast (1.31.2)
81
+ parser (>= 3.3.0.4)
82
+ rubocop-shopify (2.9.0)
83
+ rubocop (~> 1.33)
84
+ ruby-progressbar (1.13.0)
85
+ ruby2_keywords (0.0.5)
86
+ securerandom (0.3.1)
87
+ spy (1.0.5)
88
+ stackprof (0.2.26)
89
+ strscan (3.1.0)
90
+ timeout (0.4.1)
91
+ tzinfo (2.0.6)
92
+ concurrent-ruby (~> 1.0)
93
+ unicode-display_width (2.5.0)
94
+
95
+ PLATFORMS
96
+ arm64-darwin-22
97
+ arm64-darwin-23
98
+ x86_64-linux
99
+
100
+ DEPENDENCIES
101
+ byebug
102
+ cityhash (~> 0.6.0)
103
+ dalli (~> 3.2.3)
104
+ identity_cache!
105
+ memcached!
106
+ memcached_store (~> 2.3.2)
107
+ minitest (~> 5.14)
108
+ mocha (~> 2.0)
109
+ mysql2 (~> 0.5.3)
110
+ pg (>= 0.18, < 2.0)
111
+ rake (~> 13.0)
112
+ rubocop (~> 1.61.0)
113
+ rubocop-shopify (~> 2.9.0)
114
+ spy (~> 1.0)
115
+ stackprof
116
+
117
+ BUNDLED WITH
118
+ 2.4.22
data/README.md CHANGED
@@ -101,7 +101,15 @@ product = Product.fetch_by_handle(handle)
101
101
  # Fetch multiple products by providing an array of index values.
102
102
  products = Product.fetch_multi_by_handle(handles)
103
103
 
104
+ # Fetch a single product by providing composite attributes.
104
105
  products = Product.fetch_by_vendor_and_product_type(vendor, product_type)
106
+
107
+ # Fetch multiple products by providing an array of composite attributes.
108
+ products = Product.fetch_multi_by_vendor_and_product_type([
109
+ [vendor_1, product_type_1],
110
+ [vendor_2, product_type_2],
111
+ # ...
112
+ ])
105
113
  ```
106
114
 
107
115
  This gives you a lot of freedom to use your objects the way you want to, and doesn't get in your way. This does keep an independent cache copy in Memcached so you might want to watch the number of different caches that are being added.
@@ -254,11 +262,26 @@ Cache keys include a version number by default, specified in `IdentityCache::CAC
254
262
 
255
263
  ## Caveats
256
264
 
257
- A word of warning. If an `after_commit` fails before the cache expiry `after_commit` the cache will not be expired and you will be left with stale data.
258
-
259
- Since everything is being marshalled and unmarshalled from Memcached changing Ruby or Rails versions could mean your objects cannot be unmarshalled from Memcached. There are a number of ways to get around this such as namespacing keys when you upgrade or rescuing marshal load errors and treating it as a cache miss. Just something to be aware of if you are using IdentityCache and upgrade Ruby or Rails.
260
-
261
- IdentityCache is also very much _opt-in_ by deliberate design. This means IdentityCache does not mess with the way normal Rails associations work, and including it in a model won't change any clients of that model until you switch them to use `fetch` instead of `find`. This is because there is no way IdentityCache is ever going to be 100% consistent. Processes die, exceptions happen, and network blips occur, which means there is a chance that some database transaction might commit but the corresponding memcached cache invalidation operation does not make it. This means that you need to think carefully about when you use `fetch` and when you use `find`. For example, at Shopify, we never use any `fetch`ers on the path which moves money around, because IdentityCache could simply be wrong, and we want to charge people the right amount of money. We do however use the fetchers on performance critical paths where absolute correctness isn't the most important thing, and this is what IdentityCache is intended for.
265
+ IdentityCache is never going to be 100% consistent, since cache invalidations can be lost. As such, it was intentionally designed to be _opt-in_, so it is only used where cache inconsistency is tolerated. This means IdentityCache does not mess with the way normal Rails associations work, and including it in a model won't change any clients of that model until you switch them to use `fetch` instead of `find`. This means that you need to think carefully about when you use `fetch` and when you use `find`.
266
+
267
+ Expected sources of lost cache invalidations include:
268
+ * Database write performed that doesn't trigger an after_commit callback
269
+ * Process/system getting killed or crashing between the database commit and cache invalidation
270
+ * Network unavailability, including transient failures, preventing the delivery of the cache invalidation
271
+ * Memcached unavailability or failure preventing the processing of the cache invalidation request
272
+ * Memcached flush / restart could remove a cache invalidation that would normally interrupt a cache fill that started when the cache key was absent. E.g.
273
+ 1. cache key absent (not just invalidated)
274
+ 2. process 1 reads cache key
275
+ 3. process 1 starts reading from the database
276
+ 4. process 2 writes to the database
277
+ 5. process 2 writes a cache invalidation marker to cache key
278
+ 6. memcached flush
279
+ 7. process 1 uses an `ADD` operation, which succeeds in filling the cache with the now stale data
280
+ * Rollout of cache namespace changes (e.g. from upgrading IdentityCache, adding columns, cached associations or from application changes to IdentityCache.cache_namespace) can result in cache fills to the new namespace that aren't invalidated by cache invalidations from a process still using the old namespace
281
+
282
+ Cache expiration is meant to be used to help the system recover, but it only works if the application avoids using the cache data as a transaction to write data. IdentityCache avoids loading cached data from its methods during an open transaction, but can't prevent cache data that was loaded before the transaction was opened from being used in a transaction. IdentityCache won't help with scaling write traffic, it was intended for scaling database queries from read-only requests.
283
+
284
+ IdentityCache also caches the absence of database values (e.g. to avoid performance problems when it is destroyed), so lost cache invalidations can also result in that value continuing to remain absent. As such, avoid sending the id of an uncommitted database record to another process (e.g. queuing it to a background job), since that could result in an attempt to read the record by its id before it has been created. A cache invalidation will still be attempted when the record is created, but that could be lost.
262
285
 
263
286
  ## Notes
264
287
 
data/Rakefile CHANGED
@@ -9,14 +9,82 @@ require "rdoc/task"
9
9
  desc("Default: run tests and style checks.")
10
10
  task(default: [:test, :rubocop])
11
11
 
12
- desc("Test the identity_cache plugin.")
13
- Rake::TestTask.new(:test) do |t|
14
- t.libs << "lib"
15
- t.libs << "test"
16
- t.pattern = "test/**/*_test.rb"
17
- t.verbose = true
12
+ namespace :test do
13
+ desc "Test the identity_cache plugin with default Gemfile"
14
+ Rake::TestTask.new(:default) do |t|
15
+ t.libs << "lib"
16
+ t.libs << "test"
17
+ t.pattern = "test/**/*_test.rb"
18
+ t.verbose = true
19
+ end
20
+
21
+ desc "Test the identity_cache plugin with minimum supported dependencies"
22
+ task :min_supported do
23
+ gemfile = File.expand_path("gemfiles/Gemfile.min-supported", __dir__)
24
+ ENV["BUNDLE_GEMFILE"] = gemfile
25
+
26
+ puts "\nInstalling dependencies for #{gemfile}..."
27
+ Bundler.with_unbundled_env do
28
+ system("bundle install --gemfile #{gemfile}") || abort("Bundle install failed")
29
+ end
30
+
31
+ puts "Running tests with #{gemfile}..."
32
+ Rake::TestTask.new(:run_min_supported) do |t|
33
+ t.libs << "lib"
34
+ t.libs << "test"
35
+ t.pattern = "test/**/*_test.rb"
36
+ t.verbose = true
37
+ end
38
+ Rake::Task["run_min_supported"].invoke
39
+ end
40
+
41
+ desc "Test the identity_cache plugin with latest released dependencies"
42
+ task :latest_release do
43
+ gemfile = File.expand_path("gemfiles/Gemfile.latest-release", __dir__)
44
+ ENV["BUNDLE_GEMFILE"] = gemfile
45
+
46
+ puts "\nInstalling dependencies for #{gemfile}..."
47
+ Bundler.with_unbundled_env do
48
+ system("bundle install --gemfile #{gemfile}") || abort("Bundle install failed")
49
+ end
50
+
51
+ puts "Running tests with #{gemfile}..."
52
+ Rake::TestTask.new(:run_latest_release) do |t|
53
+ t.libs << "lib"
54
+ t.libs << "test"
55
+ t.pattern = "test/**/*_test.rb"
56
+ t.verbose = true
57
+ end
58
+ Rake::Task["run_latest_release"].invoke
59
+ end
60
+
61
+ desc "Test the identity_cache plugin with rails edge dependencies"
62
+ task :rails_edge do
63
+ gemfile = File.expand_path("gemfiles/Gemfile.rails-edge", __dir__)
64
+ ENV["BUNDLE_GEMFILE"] = gemfile
65
+
66
+ puts "\nInstalling dependencies for #{gemfile}..."
67
+ Bundler.with_unbundled_env do
68
+ system("bundle install --gemfile #{gemfile}") || abort("Bundle install failed")
69
+ end
70
+
71
+ puts "Running tests with #{gemfile}..."
72
+ Rake::TestTask.new(:run_rails_edge) do |t|
73
+ t.libs << "lib"
74
+ t.libs << "test"
75
+ t.pattern = "test/**/*_test.rb"
76
+ t.verbose = true
77
+ end
78
+ Rake::Task["run_rails_edge"].invoke
79
+ end
18
80
  end
19
81
 
82
+ desc "Run default tests"
83
+ task test: ["test:default"]
84
+
85
+ desc "Run all tests (default, min_supported, latest_release, rails_edge)"
86
+ task test_all: ["test:default", "test:min_supported", "test:latest_release", "test:rails_edge"]
87
+
20
88
  task :rubocop do
21
89
  require "rubocop/rake_task"
22
90
  RuboCop::RakeTask.new
@@ -47,3 +115,27 @@ namespace :profile do
47
115
  ruby "./performance/profile.rb"
48
116
  end
49
117
  end
118
+
119
+ namespace :db do
120
+ desc "Create the identity_cache_test database"
121
+ task :create do
122
+ require "mysql2"
123
+
124
+ config = {
125
+ host: ENV.fetch("MYSQL_HOST") || "localhost",
126
+ port: ENV.fetch("MYSQL_PORT") || 1037,
127
+ username: ENV.fetch("MYSQL_USER") || "root",
128
+ password: ENV.fetch("MYSQL_PASSWORD") || "",
129
+ }
130
+
131
+ begin
132
+ client = Mysql2::Client.new(config)
133
+ client.query("CREATE DATABASE IF NOT EXISTS identity_cache_test")
134
+ puts "Database 'identity_cache_test' created successfully. host: #{config[:host]}, port: #{config[:port]}"
135
+ rescue Mysql2::Error => e
136
+ puts "Error creating database: #{e.message}"
137
+ ensure
138
+ client&.close
139
+ end
140
+ end
141
+ end
data/dev.yml CHANGED
@@ -1,18 +1,14 @@
1
1
  name: identity-cache
2
2
 
3
3
  up:
4
- - homebrew:
5
- - mysql-client@5.7:
6
- or: [mysql@5.7]
7
- conflicts: [mysql-connector-c, mysql, mysql-client]
8
- - ruby: 2.7.2
9
- - isogun
4
+ - ruby
10
5
  - bundler
11
-
12
- env:
13
- RAILGUN_HOST: identity-cache.railgun
14
- MYSQL_HOST: identity-cache.railgun
15
- MEMCACHED_HOST: identity-cache.railgun
6
+ - memcached
7
+ - mysql
8
+ - custom:
9
+ name: create database identity_cache_test
10
+ met?: mysql -u root -h 127.0.0.1 -P 1037 -e "SHOW DATABASES;" | grep identity_cache_test
11
+ meet: bundle exec rake db:create
16
12
 
17
13
  commands:
18
14
  test:
@@ -20,7 +16,7 @@ commands:
20
16
  optional:
21
17
  argument: file
22
18
  optional: args...
23
- desc: 'Run tests'
19
+ desc: "Run tests"
24
20
  run: |
25
21
  if [[ $# -eq 0 ]]; then
26
22
  bundle exec rake test
@@ -28,22 +24,26 @@ commands:
28
24
  bundle exec ruby -I test "$@"
29
25
  fi
30
26
 
27
+ test-all:
28
+ desc: "Run tests for all gemfiles (default, min_supported, latest_release, rails_edge)"
29
+ run: bundle exec rake test_all
30
+
31
31
  style:
32
- desc: 'Run rubocop checks'
32
+ desc: "Run rubocop checks"
33
33
  run: bundle exec rubocop "$@"
34
34
 
35
35
  check:
36
- desc: 'Run tests and style checks'
36
+ desc: "Run tests and style checks"
37
37
  run: bundle exec rake test && bundle exec rubocop
38
38
 
39
39
  benchmark-cpu:
40
- desc: 'Run the identity cache CPU benchmark'
40
+ desc: "Run the identity cache CPU benchmark"
41
41
  run: bundle exec rake benchmark:cpu
42
42
 
43
43
  profile:
44
- desc: 'Profile IDC code'
44
+ desc: "Profile IDC code"
45
45
  run: bundle exec rake profile:run
46
46
 
47
47
  update-serialization-format:
48
- desc: 'Update serialization format test fixture'
48
+ desc: "Update serialization format test fixture"
49
49
  run: bundle exec rake update_serialization_format
@@ -8,6 +8,7 @@ gem "activerecord"
8
8
  gem "activesupport"
9
9
  gem "mysql2", "~> 0.5"
10
10
  gem "pg", "~> 1.1"
11
+ gem "memcached", github: "Shopify/memcached", branch: "1-0-stable-shopify"
11
12
  gem "memcached_store"
12
13
  gem "dalli"
13
14
  gem "cityhash"
@@ -3,10 +3,10 @@ gemspec path: ".."
3
3
 
4
4
  gem "ar_transaction_changes", "~> 1.1.0"
5
5
 
6
- gem "activerecord", "~> 5.2.0"
7
- gem "mysql2", "~> 0.4.4"
8
- gem "pg", "~> 0.18.0"
9
- gem "memcached", "~> 1.8.0"
10
- gem "memcached_store", "~> 1.0.0"
6
+ gem "activerecord", "~> 7.0.0"
7
+ gem "mysql2", "~> 0.5"
8
+ gem "pg", "~> 1.1"
9
+ gem "memcached", github: "Shopify/memcached", branch: "1-0-stable-shopify"
10
+ gem "memcached_store", "~> 2.3.2"
11
11
  gem "dalli", "~> 2.7.11"
12
12
  gem "cityhash", "~> 0.6.0"
@@ -5,6 +5,7 @@ gem "activerecord", github: "rails/rails", branch: "main"
5
5
  gem "activesupport", github: "rails/rails", branch: "main"
6
6
  gem "mysql2", "~> 0.5"
7
7
  gem "pg", "~> 1.1"
8
+ gem "memcached", github: "Shopify/memcached", branch: "1-0-stable-shopify"
8
9
  gem "memcached_store"
9
10
  gem "dalli"
10
11
  gem "cityhash"
@@ -31,15 +31,15 @@ Gem::Specification.new do |gem|
31
31
  gem.require_paths = ["lib"]
32
32
  gem.version = IdentityCache::VERSION
33
33
 
34
- gem.required_ruby_version = ">= 2.5.0"
34
+ gem.required_ruby_version = ">= 3.0.0"
35
35
 
36
36
  gem.metadata["allowed_push_host"] = "https://rubygems.org"
37
37
 
38
- gem.add_dependency("activerecord", ">= 5.2")
38
+ gem.add_dependency("activerecord", ">= 7.0")
39
39
  gem.add_dependency("ar_transaction_changes", "~> 1.1")
40
40
 
41
41
  gem.add_development_dependency("minitest", "~> 5.14")
42
- gem.add_development_dependency("mocha", "~> 1.12")
42
+ gem.add_development_dependency("mocha", "~> 2.0")
43
43
  gem.add_development_dependency("rake", "~> 13.0")
44
44
  gem.add_development_dependency("spy", "~> 1.0")
45
45
  end
@@ -14,7 +14,7 @@ module IdentityCache
14
14
  ensure_base_model
15
15
 
16
16
  unless (reflection = reflect_on_association(association))
17
- raise AssociationError, "Association named '#{association}' was not found on #{self.class}"
17
+ raise AssociationError, "Association named '#{association}' was not found on #{self}"
18
18
  end
19
19
 
20
20
  if reflection.scope