identity_cache 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +5 -4
- data/.github/workflows/cla.yml +22 -0
- data/.rubocop.yml +7 -3
- data/.spin/bootstrap +7 -0
- data/.spin/svc.yml +2 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +15 -5
- data/LICENSE +1 -1
- data/README.md +5 -6
- data/Rakefile +13 -12
- data/dev.yml +2 -4
- data/gemfiles/Gemfile.latest-release +12 -7
- data/gemfiles/Gemfile.min-supported +11 -6
- data/gemfiles/Gemfile.rails-edge +8 -5
- data/identity_cache.gemspec +15 -27
- data/{railgun.yml → isogun.yml} +0 -5
- data/lib/identity_cache/belongs_to_caching.rb +1 -0
- data/lib/identity_cache/cache_fetcher.rb +241 -16
- data/lib/identity_cache/cache_hash.rb +7 -6
- data/lib/identity_cache/cache_invalidation.rb +1 -0
- data/lib/identity_cache/cache_key_generation.rb +22 -19
- data/lib/identity_cache/cache_key_loader.rb +2 -2
- data/lib/identity_cache/cached/association.rb +2 -4
- data/lib/identity_cache/cached/attribute.rb +3 -3
- data/lib/identity_cache/cached/attribute_by_multi.rb +1 -1
- data/lib/identity_cache/cached/belongs_to.rb +3 -0
- data/lib/identity_cache/cached/embedded_fetching.rb +2 -0
- data/lib/identity_cache/cached/primary_index.rb +3 -2
- data/lib/identity_cache/cached/recursive/association.rb +2 -0
- data/lib/identity_cache/cached/recursive/has_many.rb +1 -0
- data/lib/identity_cache/cached/recursive/has_one.rb +1 -0
- data/lib/identity_cache/cached/reference/association.rb +1 -0
- data/lib/identity_cache/cached/reference/has_many.rb +1 -0
- data/lib/identity_cache/cached/reference/has_one.rb +1 -0
- data/lib/identity_cache/cached.rb +1 -0
- data/lib/identity_cache/configuration_dsl.rb +1 -0
- data/lib/identity_cache/encoder.rb +2 -1
- data/lib/identity_cache/expiry_hook.rb +1 -0
- data/lib/identity_cache/fallback_fetcher.rb +6 -1
- data/lib/identity_cache/mem_cache_store_cas.rb +15 -5
- data/lib/identity_cache/memoized_cache_proxy.rb +15 -15
- data/lib/identity_cache/parent_model_expiration.rb +3 -1
- data/lib/identity_cache/query_api.rb +3 -0
- data/lib/identity_cache/railtie.rb +1 -0
- data/lib/identity_cache/should_use_cache.rb +1 -0
- data/lib/identity_cache/version.rb +2 -1
- data/lib/identity_cache/with_primary_index.rb +37 -10
- data/lib/identity_cache/without_primary_index.rb +7 -3
- data/lib/identity_cache.rb +38 -24
- data/performance/cache_runner.rb +12 -9
- data/performance/cpu.rb +6 -5
- data/performance/externals.rb +6 -5
- data/performance/profile.rb +7 -6
- metadata +27 -123
- data/.github/probots.yml +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 561965856845fc5d5094581d735584924eda58557edfb3831c39bce2498635aa
|
4
|
+
data.tar.gz: 33a85428a3b2a298076d81a79b662b742c00a25971bf453f8f57eeab7cba2681
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a8c691d8883acfd69f11724dfff13a3ef4858a2abd2ca658659981f1d08b36dea244100c4030b8ef5d39ac8509e639fea6cd02127e09d157be636fcc2bee77a
|
7
|
+
data.tar.gz: e357c3a4f02a288a5ddb3290cbd11176937821bb3c9052fb318d9139ac253650f7018a8249b2ebc9f115015a6434baf7cb1b7e76bfb0dbf1e98ee745e3e95ed7
|
data/.github/workflows/ci.yml
CHANGED
@@ -12,17 +12,18 @@ jobs:
|
|
12
12
|
runs-on: ubuntu-latest
|
13
13
|
|
14
14
|
strategy:
|
15
|
+
fail-fast: false
|
15
16
|
matrix:
|
16
17
|
entry:
|
17
18
|
- name: 'Minimum supported'
|
18
|
-
ruby: 2.5
|
19
|
+
ruby: '2.5'
|
19
20
|
gemfile: "Gemfile.min-supported"
|
20
21
|
- name: 'Latest released & run rubocop'
|
21
|
-
ruby:
|
22
|
+
ruby: '3.0'
|
22
23
|
gemfile: "Gemfile.latest-release"
|
23
24
|
rubocop: true
|
24
25
|
- name: 'Rails edge'
|
25
|
-
ruby:
|
26
|
+
ruby: '3.0'
|
26
27
|
gemfile: "Gemfile.rails-edge"
|
27
28
|
edge: true
|
28
29
|
|
@@ -70,7 +71,7 @@ jobs:
|
|
70
71
|
sudo apt-get -y install libmemcached-dev libmysqlclient-dev libpq-dev libsasl2-dev
|
71
72
|
- uses: actions/checkout@v2
|
72
73
|
- name: Set up Ruby
|
73
|
-
uses:
|
74
|
+
uses: ruby/setup-ruby@v1
|
74
75
|
with:
|
75
76
|
ruby-version: ${{ matrix.entry.ruby }}
|
76
77
|
- name: Install bundler and gems
|
@@ -0,0 +1,22 @@
|
|
1
|
+
name: Contributor License Agreement (CLA)
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request_target:
|
5
|
+
types: [opened, synchronize]
|
6
|
+
issue_comment:
|
7
|
+
types: [created]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
cla:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
if: |
|
13
|
+
(github.event.issue.pull_request
|
14
|
+
&& !github.event.issue.pull_request.merged_at
|
15
|
+
&& contains(github.event.comment.body, 'signed')
|
16
|
+
)
|
17
|
+
|| (github.event.pull_request && !github.event.pull_request.merged)
|
18
|
+
steps:
|
19
|
+
- uses: Shopify/shopify-cla-action@v1
|
20
|
+
with:
|
21
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
22
|
+
cla-token: ${{ secrets.CLA_TOKEN }}
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
|
2
|
-
-
|
1
|
+
inherit_gem:
|
2
|
+
rubocop-shopify: rubocop.yml
|
3
3
|
|
4
4
|
AllCops:
|
5
|
-
TargetRubyVersion: 2.
|
5
|
+
TargetRubyVersion: 2.5
|
6
|
+
NewCops: disable
|
7
|
+
|
8
|
+
Layout/BeginEndAlignment:
|
9
|
+
EnforcedStyleAlignWith: start_of_line
|
data/.spin/bootstrap
ADDED
data/.spin/svc.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Identity Cache Changelog
|
2
2
|
|
3
|
+
## 1.2.0
|
4
|
+
|
5
|
+
### Fixes
|
6
|
+
- Fix mem_cache_store adapter with pool_size (#489)
|
7
|
+
- Fix dalli deprecation warning about requiring 'dalli/cas/client' (#511)
|
8
|
+
- Make transitionary method IdentityCache.with_fetch_read_only_records thread-safe (#503)
|
9
|
+
|
10
|
+
### Features
|
11
|
+
- Add support for fill lock with lock wait to avoid thundering herd problem (#373)
|
12
|
+
|
3
13
|
## 1.1.0
|
4
14
|
|
5
15
|
### Fixes
|
data/Gemfile
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
3
4
|
gemspec
|
4
5
|
|
5
|
-
gem
|
6
|
-
|
7
|
-
gem
|
8
|
-
|
6
|
+
gem "rubocop", "~> 1.5"
|
7
|
+
|
8
|
+
gem "rubocop-shopify", "~> 2.9.0", require: false
|
9
|
+
|
10
|
+
gem "mysql2", "~> 0.5.3", platform: :mri
|
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"
|
15
|
+
gem "cityhash", "~> 0.6.0", platform: :mri
|
16
|
+
|
17
|
+
gem "byebug", platform: :mri
|
18
|
+
gem "stackprof", platform: :mri
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# IdentityCache
|
2
|
-
[![Build Status](https://
|
2
|
+
[![Build Status](https://github.com/Shopify/identity_cache/workflows/CI/badge.svg?branch=main)](https://github.com/Shopify/identity_cache/actions?query=branch%3Amain)
|
3
3
|
|
4
4
|
Opt in read through ActiveRecord caching used in production and extracted from Shopify. IdentityCache lets you specify how you want to cache your model objects, at the model level, and adds a number of convenience methods for accessing those objects through the cache. Memcached is used as the backend cache store, and the database is only hit when a copy of the object cannot be found in Memcached.
|
5
5
|
|
@@ -29,7 +29,7 @@ Add the following to all your environment/*.rb files (production/development/tes
|
|
29
29
|
|
30
30
|
```ruby
|
31
31
|
config.identity_cache_store = :mem_cache_store, "mem1.server.com", "mem2.server.com", {
|
32
|
-
expires_in: 6.hours.to_i, # in case of network errors when sending a
|
32
|
+
expires_in: 6.hours.to_i, # in case of network errors when sending a cache invalidation
|
33
33
|
failover: false, # avoids more cache consistency issues
|
34
34
|
}
|
35
35
|
```
|
@@ -48,7 +48,7 @@ config.identity_cache_store = :memcached_store,
|
|
48
48
|
Memcached.new(["mem1.server.com"],
|
49
49
|
support_cas: true,
|
50
50
|
auto_eject_hosts: false, # avoids more cache consistency issues
|
51
|
-
), { expires_in: 6.hours.to_i } # in case of network errors when sending a
|
51
|
+
), { expires_in: 6.hours.to_i } # in case of network errors when sending a cache invalidation
|
52
52
|
```
|
53
53
|
|
54
54
|
Add an initializer with this code:
|
@@ -254,14 +254,13 @@ Cache keys include a version number by default, specified in `IdentityCache::CAC
|
|
254
254
|
|
255
255
|
## Caveats
|
256
256
|
|
257
|
-
A word of warning.
|
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
258
|
|
259
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
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
|
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.
|
262
262
|
|
263
263
|
## Notes
|
264
264
|
|
265
|
-
- JRuby will not work with this current version, as we are using the memcached gem internally to interface with memcache.
|
266
265
|
- See CHANGELOG.md for a list of changes to the library over time.
|
267
266
|
- The library is MIT licensed and we welcome contributions. See CONTRIBUTING.md for more information.
|
data/Rakefile
CHANGED
@@ -1,31 +1,32 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
# frozen_string_literal: true
|
3
|
-
require 'bundler/gem_tasks'
|
4
3
|
|
5
|
-
require
|
6
|
-
require 'rdoc/task'
|
4
|
+
require "bundler/gem_tasks"
|
7
5
|
|
8
|
-
|
6
|
+
require "rake/testtask"
|
7
|
+
require "rdoc/task"
|
8
|
+
|
9
|
+
desc("Default: run tests and style checks.")
|
9
10
|
task(default: [:test, :rubocop])
|
10
11
|
|
11
|
-
desc(
|
12
|
+
desc("Test the identity_cache plugin.")
|
12
13
|
Rake::TestTask.new(:test) do |t|
|
13
|
-
t.libs <<
|
14
|
-
t.libs <<
|
15
|
-
t.pattern =
|
14
|
+
t.libs << "lib"
|
15
|
+
t.libs << "test"
|
16
|
+
t.pattern = "test/**/*_test.rb"
|
16
17
|
t.verbose = true
|
17
18
|
end
|
18
19
|
|
19
20
|
task :rubocop do
|
20
|
-
require
|
21
|
+
require "rubocop/rake_task"
|
21
22
|
RuboCop::RakeTask.new
|
22
23
|
end
|
23
24
|
|
24
|
-
desc(
|
25
|
+
desc("Update serialization format test fixture.")
|
25
26
|
task :update_serialization_format do
|
26
|
-
|
27
|
+
["mysql2", "postgresql"].each do |db|
|
27
28
|
ENV["DB"] = db
|
28
|
-
ruby
|
29
|
+
ruby "./test/helpers/update_serialization_format.rb"
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
data/dev.yml
CHANGED
@@ -2,18 +2,16 @@ name: identity-cache
|
|
2
2
|
|
3
3
|
up:
|
4
4
|
- homebrew:
|
5
|
-
- postgresql
|
6
5
|
- mysql-client@5.7:
|
7
6
|
or: [mysql@5.7]
|
8
7
|
conflicts: [mysql-connector-c, mysql, mysql-client]
|
9
|
-
- ruby: 2.
|
10
|
-
-
|
8
|
+
- ruby: 2.7.2
|
9
|
+
- isogun
|
11
10
|
- bundler
|
12
11
|
|
13
12
|
env:
|
14
13
|
RAILGUN_HOST: identity-cache.railgun
|
15
14
|
MYSQL_HOST: identity-cache.railgun
|
16
|
-
POSTGRES_HOST: identity-cache.railgun
|
17
15
|
MEMCACHED_HOST: identity-cache.railgun
|
18
16
|
|
19
17
|
commands:
|
@@ -1,8 +1,13 @@
|
|
1
|
-
source
|
2
|
-
gemspec path:
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec path: ".."
|
3
3
|
|
4
|
-
gem
|
5
|
-
gem
|
6
|
-
|
7
|
-
gem "
|
8
|
-
gem
|
4
|
+
gem "rubocop", "~> 1.5"
|
5
|
+
gem "rubocop-shopify", "~> 2.9.0", require: false
|
6
|
+
|
7
|
+
gem "activerecord"
|
8
|
+
gem "activesupport"
|
9
|
+
gem "mysql2", "~> 0.5"
|
10
|
+
gem "pg", "~> 1.1"
|
11
|
+
gem "memcached_store"
|
12
|
+
gem "dalli"
|
13
|
+
gem "cityhash"
|
@@ -1,7 +1,12 @@
|
|
1
|
-
source
|
2
|
-
gemspec path:
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec path: ".."
|
3
3
|
|
4
|
-
gem
|
5
|
-
|
6
|
-
gem
|
7
|
-
gem
|
4
|
+
gem "ar_transaction_changes", "~> 1.1.0"
|
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"
|
11
|
+
gem "dalli", "~> 2.7.11"
|
12
|
+
gem "cityhash", "~> 0.6.0"
|
data/gemfiles/Gemfile.rails-edge
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
source
|
2
|
-
gemspec path:
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec path: ".."
|
3
3
|
|
4
|
-
gem
|
5
|
-
gem
|
6
|
-
gem "mysql2", "
|
4
|
+
gem "activerecord", github: "rails/rails", branch: "main"
|
5
|
+
gem "activesupport", github: "rails/rails", branch: "main"
|
6
|
+
gem "mysql2", "~> 0.5"
|
7
7
|
gem "pg", "~> 1.1"
|
8
|
+
gem "memcached_store"
|
9
|
+
gem "dalli"
|
10
|
+
gem "cityhash"
|
data/identity_cache.gemspec
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# frozen_string_literal: true
|
3
|
-
|
3
|
+
|
4
|
+
require File.expand_path("../lib/identity_cache/version", __FILE__)
|
4
5
|
|
5
6
|
Gem::Specification.new do |gem|
|
6
7
|
gem.authors = [
|
@@ -15,43 +16,30 @@ Gem::Specification.new do |gem|
|
|
15
16
|
gem.email = ["gems@shopify.com"]
|
16
17
|
gem.description = "Opt-in read through Active Record caching."
|
17
18
|
gem.summary = "IdentityCache lets you specify how you want to cache your " \
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
"model objects, at the model level, and adds a number of " \
|
20
|
+
"convenience methods for accessing those objects through " \
|
21
|
+
"the cache. Memcached is used as the backend cache store, " \
|
22
|
+
"and the database is only hit when a copy of the object " \
|
23
|
+
"cannot be found in Memcached."
|
23
24
|
gem.homepage = "https://github.com/Shopify/identity_cache"
|
24
25
|
|
25
26
|
gem.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
27
|
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^test/}) }
|
27
28
|
end
|
28
29
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
29
|
-
gem.test_files = gem.files.grep(%r{^test/})
|
30
30
|
gem.name = "identity_cache"
|
31
31
|
gem.require_paths = ["lib"]
|
32
32
|
gem.version = IdentityCache::VERSION
|
33
33
|
|
34
|
-
gem.required_ruby_version =
|
35
|
-
|
36
|
-
gem.metadata['allowed_push_host'] = 'https://rubygems.org'
|
34
|
+
gem.required_ruby_version = ">= 2.5.0"
|
37
35
|
|
38
|
-
gem.
|
39
|
-
gem.add_dependency('activerecord', '>= 5.2')
|
36
|
+
gem.metadata["allowed_push_host"] = "https://rubygems.org"
|
40
37
|
|
41
|
-
gem.
|
42
|
-
gem.
|
43
|
-
gem.add_development_dependency('dalli')
|
44
|
-
gem.add_development_dependency('rake')
|
45
|
-
gem.add_development_dependency('mocha', '0.14.0')
|
46
|
-
gem.add_development_dependency('spy')
|
47
|
-
gem.add_development_dependency('minitest', '>= 2.11.0')
|
38
|
+
gem.add_dependency("activerecord", ">= 5.2")
|
39
|
+
gem.add_dependency("ar_transaction_changes", "~> 1.1")
|
48
40
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
gem.add_development_dependency('mysql2')
|
54
|
-
gem.add_development_dependency('pg')
|
55
|
-
gem.add_development_dependency('stackprof')
|
56
|
-
end
|
41
|
+
gem.add_development_dependency("minitest", "~> 5.14")
|
42
|
+
gem.add_development_dependency("mocha", "~> 1.12")
|
43
|
+
gem.add_development_dependency("rake", "~> 13.0")
|
44
|
+
gem.add_development_dependency("spy", "~> 1.0")
|
57
45
|
end
|
data/{railgun.yml → isogun.yml}
RENAMED