exercism-config 0.131.0 → 0.132.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: 022a04eecafa535e9bdc8c6b9318ec6d6cfbffc4a52414a3865ef2f60e924497
4
- data.tar.gz: cf41e3c8c92f94299eb4b8fd421883548c323a059127901a2b5ef6aacc8e72f2
3
+ metadata.gz: a9e8983e2c22effab8a09ab797692425b22617a659a2120dea4ca17d0c4c539e
4
+ data.tar.gz: 8311ced800943c7606457584c44a5b2d007425aff04bdf7de77d840944a5f0d6
5
5
  SHA512:
6
- metadata.gz: aa6b5336b973c833c469b574f211c1b662b9ab45bc4cdd0318a61fae74c8c4008580660817f5272828d851df10dec6d63abc785817220676d2f17755952e8664
7
- data.tar.gz: c98758723a9a86d477a7110248c4f8d20bd8a0a5d891f64a75dfe9648d1d99962232eac13efb8cda21a689d725b2d903907feb0fe01ab257c8449fb9e928ca9f
6
+ metadata.gz: 061d7ac2ae9620178592a652618ff06d6bb1f5fd02cfd0ebce062a521f72c970d39c91ee9ba3631ac5a78293920015e69f6160584a6225b458ecefa2a457aab9
7
+ data.tar.gz: fbea37c2ef414c36f3dee867851868c30501f2afcb3a392c182e9147505b90015cb288f99fe0e255ffbd9dc3cf793ef0c38e721442e915e15a4730c34f214b74
@@ -43,6 +43,7 @@ Gem::Specification.new do |spec|
43
43
  spec.add_development_dependency 'aws-sdk-ecr'
44
44
  spec.add_development_dependency 'aws-sdk-s3'
45
45
  spec.add_development_dependency 'aws-sdk-sesv2'
46
+ spec.add_development_dependency 'connection_pool'
46
47
  spec.add_development_dependency 'discourse_api'
47
48
  spec.add_development_dependency 'opensearch-ruby', '2.1.0'
48
49
  spec.add_development_dependency 'redis', '~> 5.1'
@@ -0,0 +1,54 @@
1
+ module Exercism
2
+ # A registry of pooled Redis clients, keyed by name.
3
+ #
4
+ # Clients used to be built per call, which turned out to be expensive rather
5
+ # than merely wasteful. Redis::Cluster is eager: RedisClient::Cluster#initialize
6
+ # builds its router inline, and the router does topology discovery and opens
7
+ # node connections there and then. Because nothing held a reference to the
8
+ # client afterwards, every command paid a connect plus a full slot-map fetch
9
+ # and then threw the connection away - putting a handshake on the critical
10
+ # path of every tooling job state transition, and (because the orchestrator
11
+ # does the same on each idle poll) accounting for ~96% of our ElastiCache
12
+ # ECPU bill.
13
+ #
14
+ # A single shared client would fix the churn, but Redis serialises every
15
+ # command through an internal Monitor, so sharing one across the
16
+ # orchestrator's and Sidekiq's threads would trade connection cost for
17
+ # contention. Hence a pool.
18
+ module RedisPool
19
+ DEFAULT_SIZE = 5
20
+ DEFAULT_TIMEOUT = 5
21
+
22
+ @mutex = Mutex.new
23
+ @pools = {}
24
+ @pid = nil
25
+
26
+ # Returns the pool for +key+, building it from the block on first use.
27
+ # The returned object quacks like a Redis client: ConnectionPool::Wrapper
28
+ # checks a connection out for the duration of each method call.
29
+ def self.for(key, &)
30
+ require 'connection_pool'
31
+
32
+ @mutex.synchronize do
33
+ reset_after_fork!
34
+
35
+ @pools[key] ||= ConnectionPool::Wrapper.new(size:, timeout: DEFAULT_TIMEOUT, &)
36
+ end
37
+ end
38
+
39
+ def self.size
40
+ Integer(ENV.fetch('EXERCISM_REDIS_POOL_SIZE', DEFAULT_SIZE))
41
+ end
42
+
43
+ # A pool must never be shared across a fork - parent and child would write
44
+ # down each other's sockets - so pools are scoped to the pid and rebuilt
45
+ # from scratch in the child.
46
+ def self.reset_after_fork!
47
+ return if @pid == Process.pid
48
+
49
+ @pools = {}
50
+ @pid = Process.pid
51
+ end
52
+ private_class_method :reset_after_fork!
53
+ end
54
+ end
data/lib/exercism.rb CHANGED
@@ -13,9 +13,10 @@ module Exercism
13
13
  @secrets ||= ExercismConfig::RetrieveSecrets.()
14
14
  end
15
15
 
16
- def self.redis_tooling_client = redis_client(config.tooling_redis_url)
17
- def self.redis_git_cache_client = redis_client(config.git_cache_redis_url)
18
- def self.redis_cache_client = redis_client(config.cache_redis_url)
16
+ # Pooled rather than built per call - see Exercism::RedisPool for why.
17
+ def self.redis_tooling_client = RedisPool.for(:tooling) { redis_client(config.tooling_redis_url) }
18
+ def self.redis_git_cache_client = RedisPool.for(:git_cache) { redis_client(config.git_cache_redis_url) }
19
+ def self.redis_cache_client = RedisPool.for(:cache) { redis_client(config.cache_redis_url) }
19
20
 
20
21
  def self.redis_client(url)
21
22
  require 'redis'
@@ -1,3 +1,3 @@
1
1
  module ExercismConfig
2
- VERSION = '0.131.0'.freeze
2
+ VERSION = '0.132.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exercism-config
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.131.0
4
+ version: 0.132.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Walker
@@ -191,6 +191,20 @@ dependencies:
191
191
  - - ">="
192
192
  - !ruby/object:Gem::Version
193
193
  version: '0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: connection_pool
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ type: :development
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
194
208
  - !ruby/object:Gem::Dependency
195
209
  name: discourse_api
196
210
  requirement: !ruby/object:Gem::Requirement
@@ -293,6 +307,7 @@ files:
293
307
  - lib/exercism-config.rb
294
308
  - lib/exercism.rb
295
309
  - lib/exercism/config.rb
310
+ - lib/exercism/redis_pool.rb
296
311
  - lib/exercism/secrets.rb
297
312
  - lib/exercism/tooling_job.rb
298
313
  - lib/exercism_config/determine_environment.rb