chewy 8.3.0 → 8.4.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: 34d773f10bf67d60eca51ff91a6d668186eea74515bb5b31a5fd5ce814dac3c7
4
- data.tar.gz: 334c6e92185369170b9f4d534029b69c218bb3c0cabfe6e4c11fd79268c19587
3
+ metadata.gz: bbef8e814a251ebcf189865c37c82476a8e3397983c4ede20694df87a7a61bd9
4
+ data.tar.gz: e673c1ab4ae490dd3ae716bcb3e617a0661f87c3f43143f59237a46228777375
5
5
  SHA512:
6
- metadata.gz: 24bde735bcfc4f1bfec8b146cdcc5024d049e1405ccfbbae5e6f57f53a89aa9a6951063b00251a2f87fafac3632e1640fddc64038c7337e9147a0b9d6740f984
7
- data.tar.gz: d2d84b1d2d3b07bde52699dfc54ed3b9ac8b9b71c5d79ca204851abab06bf53a41f0591ded6cdab59be5bf87522374dc10ff01d180891bcf81d73d33b009e180
6
+ metadata.gz: '04927525ee47f4f5591ed3f826d2477a87f267be465b739070c82cf1c54d558fe77d56bc88868d35be5fa6c27c8a0302ac31b6fd1f7c79c18b0759eb3e14eabf'
7
+ data.tar.gz: 62c30c3643ed78e661409929e5a075f02cbac789978f84d0a89531e00ab90764782dc085a7a44243b53b5613466794baa4b881eb22c55abdbde7b46bbb583966
data/CHANGELOG.md CHANGED
@@ -8,6 +8,28 @@
8
8
 
9
9
  ### Changes
10
10
 
11
+ ## 8.4.0 (2026-06-17)
12
+
13
+ ### New Features
14
+
15
+ * [#1036](https://github.com/toptal/chewy/issues/1036): Add `Chewy.close_client` and `Chewy::ElasticClient#close` to explicitly close connections to Elasticsearch, avoiding file descriptor leaks in long-lived multi-threaded processes (e.g. Sidekiq). ([@AlfonsoUceda][])
16
+
17
+ ### Bug Fixes
18
+
19
+ * ([#1034](https://github.com/toptal/chewy/issues/1034)): Fixed `delayed_sidekiq` strategy raising `TypeError: Unsupported command argument type: Array` on Sidekiq 7+. The LUA scripts now run via `EVALSHA` through a portable helper instead of redis-rb's keyword `eval`, which the redis-client-backed client Sidekiq 7+ yields does not support. ([@AlfonsoUceda][])
20
+
21
+ ### Changes
22
+
23
+ ## 8.3.1 (2026-06-05)
24
+
25
+ ### New Features
26
+
27
+ ### Bug Fixes
28
+
29
+ * [#1037](https://github.com/toptal/chewy/pull/1037): `#scroll_batches` now clears the scroll context opened for an empty result. The `_scroll_id` was only captured while iterating batches, so a zero-hit scope (which still opens a context on the initial request) leaked it until the scroll keepalive expired, contributing to `search.max_open_scroll_context` exhaustion.
30
+
31
+ ### Changes
32
+
11
33
  ## 8.3.0 (2026-06-03)
12
34
 
13
35
  ### New Features
@@ -888,6 +910,7 @@
888
910
  [@AgeevAndrew]: https://github.com/AgeevAndrew
889
911
  [@aglushkov]: https://github.com/aglushkov
890
912
  [@AlexVPopov]: https://github.com/AlexVPopov
913
+ [@AlfonsoUceda]: https://github.com/AlfonsoUceda
891
914
  [@AndreySavelyev]: https://github.com/AndreySavelyev
892
915
  [@afg419]: https://github.com/afg419
893
916
  [@arion]: https://github.com/arion
data/README.md CHANGED
@@ -133,6 +133,23 @@ development:
133
133
  ca_file: './tmp/http_ca.crt'
134
134
  ```
135
135
 
136
+ ### Closing connections
137
+
138
+ `Chewy.client` is memoized per thread, so every thread that touches Chewy gets
139
+ its own client with its own connections to Elasticsearch. Neither
140
+ `elasticsearch-ruby` nor `elastic-transport` expose a public way to close those
141
+ connections, so they are only released when Ruby's garbage collector reclaims
142
+ the client. In long-lived, multi-threaded processes that frequently spawn and
143
+ discard threads (e.g. Sidekiq, which replaces a thread on job failure), this can
144
+ leak file descriptors.
145
+
146
+ Use `Chewy.close_client` to close the current thread's connections and drop its
147
+ client. The next `Chewy.client` call rebuilds a fresh one:
148
+
149
+ ```ruby
150
+ Chewy.close_client
151
+ ```
152
+
136
153
  ### Index
137
154
 
138
155
  Create `app/chewy/users_index.rb` with User Index:
@@ -12,6 +12,21 @@ module Chewy
12
12
  @elastic_client = elastic_client
13
13
  end
14
14
 
15
+ # Closes the underlying connections to Elasticsearch.
16
+ #
17
+ # Neither elasticsearch-ruby nor elastic-transport expose a public method
18
+ # to close connections, so they are only released when Ruby's garbage
19
+ # collector reclaims the client instance. This reaches down to the Faraday
20
+ # connection of every transport connection and closes it explicitly, which
21
+ # is useful to avoid file descriptor leaks in long-lived processes that
22
+ # build a client per thread (e.g. Sidekiq workers).
23
+ def close
24
+ @elastic_client.transport.connections.each do |connection|
25
+ faraday = connection.connection
26
+ faraday.close if faraday.respond_to?(:close)
27
+ end
28
+ end
29
+
15
30
  private
16
31
 
17
32
  def method_missing(name, *args, **kwargs, &block)
@@ -47,7 +47,7 @@ module Chewy
47
47
  end
48
48
 
49
49
  def index_entry(object)
50
- return [] if @fields&.empty?
50
+ return [] if @fields && @fields.empty?
51
51
 
52
52
  entry = {}
53
53
  entry[:_id] = index_object_ids[object] if index_object_ids[object]
@@ -35,7 +35,7 @@ module Chewy
35
35
 
36
36
  total_batches += 1 if last_batch_size != 0
37
37
 
38
- scroll_id = nil
38
+ scroll_id = result['_scroll_id']
39
39
 
40
40
  total_batches.times do |batch_counter|
41
41
  last_run = total_batches - 1 == batch_counter
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chewy
4
+ class Strategy
5
+ class DelayedSidekiq
6
+ # Runs a LUA script through whatever client `Sidekiq.redis` yields.
7
+ #
8
+ # redis-rb (Sidekiq <= 6) accepts `eval(script, keys:, argv:)`, but the
9
+ # real Sidekiq 7+ runtime yields a redis-client-backed CompatClient that
10
+ # has no `#eval` and forwards the keyword args as a nested array, raising
11
+ # `TypeError: Unsupported command argument type: Array` (issue #971).
12
+ #
13
+ # `evalsha(sha, keys_array, argv_array)` is the one form both clients
14
+ # expand to the flat `EVALSHA sha numkeys *keys *argv` shape, so we load
15
+ # the script once and call it by digest, reloading on NOSCRIPT.
16
+ module RedisScript
17
+ def self.call(redis, script, keys:, argv:)
18
+ reloaded = false
19
+ begin
20
+ shas[script] ||= redis.script(:load, script)
21
+ redis.evalsha(shas[script], keys, argv)
22
+ rescue StandardError => e
23
+ raise if reloaded || !e.message.to_s.include?('NOSCRIPT')
24
+
25
+ reloaded = true
26
+ shas.delete(script) # flushed cache or failover: reload and retry once
27
+ retry
28
+ end
29
+ end
30
+
31
+ def self.shas
32
+ @shas ||= {}
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -10,6 +10,7 @@ require_relative '../../index'
10
10
  module Chewy
11
11
  class Strategy
12
12
  class DelayedSidekiq
13
+ require_relative 'redis_script'
13
14
  require_relative 'worker'
14
15
 
15
16
  LUA_SCRIPT = <<~LUA
@@ -98,7 +99,7 @@ module Chewy
98
99
  def postpone
99
100
  ::Sidekiq.redis do |redis|
100
101
  # do the redis stuff in a single command to avoid concurrency issues
101
- if redis.eval(LUA_SCRIPT, keys: [timechunk_key, timechunks_key], argv: [serialize_data, at, ttl])
102
+ if RedisScript.call(redis, LUA_SCRIPT, keys: [timechunk_key, timechunks_key], argv: [serialize_data, at, ttl])
102
103
  ::Sidekiq::Client.push(
103
104
  'queue' => sidekiq_queue,
104
105
  'at' => at + margin,
@@ -39,7 +39,7 @@ module Chewy
39
39
  options[:refresh] = !Chewy.disable_refresh_async if Chewy.disable_refresh_async
40
40
 
41
41
  ::Sidekiq.redis do |redis|
42
- members = redis.eval(LUA_SCRIPT, keys: [], argv: [type, score, Scheduler::KEY_PREFIX])
42
+ members = RedisScript.call(redis, LUA_SCRIPT, keys: [], argv: [type, score, Scheduler::KEY_PREFIX])
43
43
 
44
44
  # extract ids and fields & do the reset of records
45
45
  ids, fields = extract_ids_and_fields(members)
data/lib/chewy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Chewy
2
- VERSION = '8.3.0'.freeze
2
+ VERSION = '8.4.0'.freeze
3
3
  end
data/lib/chewy.rb CHANGED
@@ -102,6 +102,21 @@ module Chewy
102
102
  Chewy.current[:chewy_client] ||= Chewy::ElasticClient.new
103
103
  end
104
104
 
105
+ # Closes the current thread's client connections to Elasticsearch and
106
+ # drops the thread-local client, so the next `Chewy.client` call builds a
107
+ # fresh one.
108
+ #
109
+ # Useful in long-lived multi-threaded processes (e.g. Sidekiq) where the
110
+ # per-thread client would otherwise keep its connections open until the
111
+ # dead thread is garbage collected, leaking file descriptors.
112
+ def close_client
113
+ client = Chewy.current[:chewy_client]
114
+ return unless client
115
+
116
+ client.close
117
+ Chewy.current[:chewy_client] = nil
118
+ end
119
+
105
120
  # Sends wait_for_status request to ElasticSearch with status
106
121
  # defined in configuration.
107
122
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chewy
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.3.0
4
+ version: 8.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toptal, LLC
@@ -174,6 +174,7 @@ files:
174
174
  - lib/chewy/strategy/base.rb
175
175
  - lib/chewy/strategy/bypass.rb
176
176
  - lib/chewy/strategy/delayed_sidekiq.rb
177
+ - lib/chewy/strategy/delayed_sidekiq/redis_script.rb
177
178
  - lib/chewy/strategy/delayed_sidekiq/scheduler.rb
178
179
  - lib/chewy/strategy/delayed_sidekiq/worker.rb
179
180
  - lib/chewy/strategy/lazy_sidekiq.rb
@@ -202,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
203
  - !ruby/object:Gem::Version
203
204
  version: '0'
204
205
  requirements: []
205
- rubygems_version: 4.0.12
206
+ rubygems_version: 4.0.14
206
207
  specification_version: 4
207
208
  summary: Elasticsearch ODM client wrapper
208
209
  test_files: []