counter_culture 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d06abb7c02b9c0bd0e9536c04d7ba1f753c944c018fe6f14bc156567567c46c
4
- data.tar.gz: dbda06f156910a7f7a807ecee156cfb36b0ec88a97ecd35cd120be564df571bd
3
+ metadata.gz: 5ab4a286fa35aeed4f39a89653ed55be5da8c2ea6255a84f8f5c60c3002a4e75
4
+ data.tar.gz: 910213339a5c0815679736c77e72b8964a678fb58495fdfd9882157b3fdaaf5b
5
5
  SHA512:
6
- metadata.gz: 2b95de040d7c944ba43bf103157144bb5cca3bc1ca309f1082189e5015b46f1fd01615296794f3cc8a27f5e0f1b499fe0744ac41e0438f21986926a4b8c38e69
7
- data.tar.gz: 9889708057fbfa61c14291124e4c71bb8f0c8f67cb9bdf24f57865cff8fc27fae5f98576ff5f99d0ada07961a4781b9e943ef0679ce30f55d7e567a1d025f2eb
6
+ metadata.gz: d42e4460ea4ad61e3ee12bdc430d9cb833772a32d02bdfe9c2fd8b78f7f06d157f06e9f50dec1efb5df6bf6c917b624f4c9c626ac43ea541a9faef3422fbc5b8
7
+ data.tar.gz: 50166340b9db761bcdf4a198d21d8c1580b26d09cf4928082885175441ef260758d909e321d30c7204e2f782730833e8153db8a3d28c879dd55d8de3d2ceb453
data/.circleci/config.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  version: 2.1
2
2
 
3
3
  orbs:
4
- ruby: circleci/ruby@1.4
4
+ ruby: circleci/ruby@1.8
5
5
 
6
6
  jobs:
7
7
  test:
@@ -19,7 +19,13 @@ jobs:
19
19
  steps:
20
20
  - checkout
21
21
  - run:
22
- name: bundle update
22
+ name: gem update --system --no-ri --no-rdoc
23
+ # bundles sporadically fail with a weird Psych error if I don't do
24
+ # this. It's annoying since it's slow but it's better than
25
+ # non-deterministic build failures.
26
+ command: sudo gem update --system
27
+ - run:
28
+ name: bundle update --no-ri --no-rdoc
23
29
  command: BUNDLE_GEMFILE=gemfiles/rails_<< parameters.rails-version >>.gemfile bundle update
24
30
  - run:
25
31
  name: install dockerize
@@ -42,7 +48,7 @@ workflows:
42
48
  - test:
43
49
  matrix:
44
50
  parameters:
45
- ruby-version: ["2.6", "2.7", "3.0"]
51
+ ruby-version: ["2.6", "2.7", "3.0", "3.1"]
46
52
  rails-version: ["5.2", "6.0", "6.1", "7.0"]
47
53
  database: ["postgresql", "sqlite3", "mysql2"]
48
54
  exclude:
@@ -55,6 +61,15 @@ workflows:
55
61
  - ruby-version: "3.0"
56
62
  rails-version: "5.2"
57
63
  database: "mysql2"
64
+ - ruby-version: "3.1"
65
+ rails-version: "5.2"
66
+ database: "postgresql"
67
+ - ruby-version: "3.1"
68
+ rails-version: "5.2"
69
+ database: "sqlite3"
70
+ - ruby-version: "3.1"
71
+ rails-version: "5.2"
72
+ database: "mysql2"
58
73
  - ruby-version: "2.6"
59
74
  rails-version: "7.0"
60
75
  database: "postgresql"
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 3.3.0 (October 11, 2022)
2
+
3
+ Improvements:
4
+ - Allow reading from replica in `counter_culture_fix_counts` (#330)
5
+ - Test against Ruby 3.1 (#357)
6
+
7
+ Bugfixes:
8
+ - Don't decrement counter cache when calling `.destroy` on an already-destroyed model (#351)
9
+ - Don't immediately call `Proc` passed to `column_names` to avoid startup issue (#352)
10
+
1
11
  ## 3.2.1 (February 24, 2022)
2
12
 
3
13
  Bugfixes:
data/README.md CHANGED
@@ -20,7 +20,7 @@ product.categories_count # => will use counter cache without query
20
20
  Add counter_culture to your Gemfile:
21
21
 
22
22
  ```ruby
23
- gem 'counter_culture', '~> 2.0'
23
+ gem 'counter_culture', '~> 3.2'
24
24
  ```
25
25
 
26
26
  Then run `bundle install`
@@ -359,6 +359,20 @@ Product.counter_culture_fix_counts start: 2001, finish: 3000
359
359
  # In worker 2, lets process from 2001 to 3000
360
360
  ```
361
361
 
362
+ #### Fix counter cache using a replica database
363
+
364
+ When fixing counter caches the number of reads usually vastly exceeds the number of writes. It can make sense to offload the road load to a replica database in this case. Rails 6 introduced [native handling of multiple database connections](https://guides.rubyonrails.org/v6.0/active_record_multiple_databases.html). You can use this to send read traffic to a read-only repliace using the option `db_connection_builder`:
365
+
366
+ ```ruby
367
+ Product.counter_culture_fix_counts db_connection_builder: proc{|reading, block|
368
+ if reading # Count calls will request a reading connection
369
+ Product.connected_to(role: :reading, &block)
370
+ else # Update all calls will request a non-reading connection
371
+ Product.connected_to(role: :writing, &block)
372
+ end
373
+ }
374
+ ```
375
+
362
376
  #### Handling dynamic column names
363
377
 
364
378
  Manually populating counter caches with dynamic column names requires additional configuration:
@@ -45,15 +45,7 @@ module CounterCulture
45
45
  @after_commit_counter_cache = []
46
46
  end
47
47
 
48
- column_names_valid = (
49
- !options[:column_names] ||
50
- options[:column_names].is_a?(Hash) ||
51
- (
52
- options[:column_names].is_a?(Proc) &&
53
- options[:column_names].call.is_a?(Hash)
54
- )
55
- )
56
- unless column_names_valid
48
+ if options[:column_names] && !(options[:column_names].is_a?(Hash) || options[:column_names].is_a?(Proc))
57
49
  raise ArgumentError.new(
58
50
  ":column_names must be a Hash of conditions and column names, " \
59
51
  "or a Proc that when called returns such a Hash"
@@ -92,7 +84,7 @@ module CounterCulture
92
84
  next if options[:exclude] && options[:exclude].include?(counter.relation)
93
85
  next if options[:only] && !options[:only].include?(counter.relation)
94
86
 
95
- reconciler_options = %i(batch_size column_name finish skip_unsupported start touch verbose where polymorphic_classes)
87
+ reconciler_options = %i(batch_size column_name db_connection_builder finish skip_unsupported start touch verbose where polymorphic_classes)
96
88
 
97
89
  reconciler = CounterCulture::Reconciler.new(counter, options.slice(*reconciler_options))
98
90
  reconciler.reconcile!
@@ -113,8 +105,10 @@ module CounterCulture
113
105
  # called by after_destroy callback
114
106
  def _update_counts_after_destroy
115
107
  self.class.after_commit_counter_cache.each do |counter|
116
- # decrement counter cache
117
- counter.change_counter_cache(self, :increment => false)
108
+ unless destroyed?
109
+ # decrement counter cache
110
+ counter.change_counter_cache(self, :increment => false)
111
+ end
118
112
  end
119
113
  end
120
114
 
@@ -84,6 +84,8 @@ module CounterCulture
84
84
  { nil => counter_cache_name }
85
85
  end
86
86
 
87
+ raise ArgumentError, ":column_names must be a Hash of conditions and column names" unless counter_column_names.is_a?(Hash)
88
+
87
89
  if options[:column_name]
88
90
  counter_column_names = counter_column_names.select do |_, v|
89
91
  options[:column_name].to_s == v.to_s
@@ -123,11 +125,13 @@ module CounterCulture
123
125
  find_in_batches_args[:start] = options[:start] if options[:start].present?
124
126
  find_in_batches_args[:finish] = options[:finish] if options[:finish].present?
125
127
 
126
- counts_query.find_in_batches(**find_in_batches_args).with_index(1) do |records, index|
127
- log "Processing batch ##{index}."
128
- # now iterate over all the models and see whether their counts are right
129
- update_count_for_batch(column_name, records)
130
- log "Finished batch ##{index}."
128
+ with_reading_db_connection do
129
+ counts_query.find_in_batches(**find_in_batches_args).with_index(1) do |records, index|
130
+ log "Processing batch ##{index}."
131
+ # now iterate over all the models and see whether their counts are right
132
+ update_count_for_batch(column_name, records)
133
+ log "Finished batch ##{index}."
134
+ end
131
135
  end
132
136
  end
133
137
  log_without_newline "\n"
@@ -161,7 +165,9 @@ module CounterCulture
161
165
  end
162
166
  end
163
167
 
164
- relation_class.where(relation_class.primary_key => record.send(relation_class.primary_key)).update_all(updates.join(', '))
168
+ with_writing_db_connection do
169
+ relation_class.where(relation_class.primary_key => record.send(relation_class.primary_key)).update_all(updates.join(', '))
170
+ end
165
171
  end
166
172
  end
167
173
  end
@@ -312,6 +318,22 @@ module CounterCulture
312
318
  string.parameterize(separator: '_')
313
319
  end
314
320
  end
321
+
322
+ def with_reading_db_connection(&block)
323
+ if builder = options[:db_connection_builder]
324
+ builder.call(true, block)
325
+ else
326
+ yield
327
+ end
328
+ end
329
+
330
+ def with_writing_db_connection(&block)
331
+ if builder = options[:db_connection_builder]
332
+ builder.call(false, block)
333
+ else
334
+ yield
335
+ end
336
+ end
315
337
  end
316
338
  end
317
339
  end
@@ -1,3 +1,3 @@
1
1
  module CounterCulture
2
- VERSION = '3.2.1'.freeze
2
+ VERSION = '3.3.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: counter_culture
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Magnus von Koeller
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-02-25 00:00:00.000000000 Z
11
+ date: 2022-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -344,7 +344,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
344
344
  - !ruby/object:Gem::Version
345
345
  version: '0'
346
346
  requirements: []
347
- rubygems_version: 3.1.4
347
+ rubygems_version: 3.3.7
348
348
  signing_key:
349
349
  specification_version: 4
350
350
  summary: Turbo-charged counter caches for your Rails app.