counter_culture 3.2.1 → 3.3.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 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.