exercism-config 0.131.0 → 0.133.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: 54034ae264aab2f8c06d15e19777cb8e4d77db325a567370e832f5df74b21eef
4
+ data.tar.gz: b8deba140712b3133104b1fd03cead4441ae2632b2fb0d9417c122b04aa2b943
5
5
  SHA512:
6
- metadata.gz: aa6b5336b973c833c469b574f211c1b662b9ab45bc4cdd0318a61fae74c8c4008580660817f5272828d851df10dec6d63abc785817220676d2f17755952e8664
7
- data.tar.gz: c98758723a9a86d477a7110248c4f8d20bd8a0a5d891f64a75dfe9648d1d99962232eac13efb8cda21a689d725b2d903907feb0fe01ab257c8449fb9e928ca9f
6
+ metadata.gz: '078267a2c405d101523090e73b5f97a268046d566a5de03c0fa07f2a163a6464c417dc555ed27fcaa6da8bd5d2a5ee073ca920fc3db4d19660ccd8ac5f15be8b'
7
+ data.tar.gz: aa752572631779e6d298f2a5b2492ce5fefc194880b131881368cae16f0c564bd30e91fb4e885a65d5a4c7fb28bac8221da947cc0932fc6b9350e936e8045750
data/README.md CHANGED
@@ -25,6 +25,7 @@ Exercism.config.tooling_orchestrator_url
25
25
  Exercism.config.language_server_url
26
26
  Exercism.config.opensearch_host
27
27
  Exercism.config.paypal_api_url
28
+ Exercism.config.assistant_chat_url
28
29
 
29
30
  # Secrets
30
31
  Exercism.secrets.github_access_token
@@ -61,6 +62,8 @@ Exercism.secrets.paypal_premium_yearly_plan_id
61
62
  Exercism.secrets.paypal_lifetime_insiders_hosted_button_id
62
63
  Exercism.secrets.chatgpt_access_token
63
64
  Exercism.secrets.sparkpost_api_key
65
+ Exercism.secrets.assistant_chat_jwt_secret
66
+ Exercism.secrets.assistant_chat_hmac_secret
64
67
  Exercism.secrets.openai_api_key
65
68
  Exercism.secrets.openai_tags_model
66
69
 
@@ -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.133.0'.freeze
3
3
  end
data/settings/ci.yml CHANGED
@@ -6,7 +6,7 @@ anycable_rpc_host: 127.0.0.1:50051
6
6
  tooling_redis_url: redis://127.0.0.1:6379/3
7
7
  snippet_generator_url: http://127.0.0.1:3024/extract_snippet
8
8
  lines_of_code_counter_url: http://127.0.0.1:3025/count_lines_of_code
9
- chatgpt_proxy_url: http://127.0.0.1:3026/ask_chatgpt
9
+ assistant_chat_url: http://127.0.0.1:3027
10
10
  tooling_cloudwatch_jobs_log_group_name: "/tooling-jobs"
11
11
  tooling_cloudwatch_jobs_log_stream_name: "general"
12
12
 
data/settings/local.yml CHANGED
@@ -6,7 +6,7 @@ anycable_rpc_host: 127.0.0.1:50051
6
6
  tooling_redis_url: redis://127.0.0.1:6379/3
7
7
  snippet_generator_url: http://local.exercism.io:3024/extract_snippet
8
8
  lines_of_code_counter_url: http://local.exercism.io:3025/count_lines_of_code
9
- chatgpt_proxy_url: http://local.exercism.io:3026/ask_chatgpt
9
+ assistant_chat_url: http://local.exercism.io:3027
10
10
  tooling_cloudwatch_jobs_log_group_name: "/tooling-jobs"
11
11
  tooling_cloudwatch_jobs_log_stream_name: "general"
12
12
 
data/settings/secrets.yml CHANGED
@@ -54,3 +54,9 @@ github_solution_syncer_private_key: "SOME-RSA-PRIVATE-KEY"
54
54
 
55
55
  cloudflare_zone_id: "fake-cloudflare-zone-id"
56
56
  cloudflare_api_token: "fake-cloudflare-api-token"
57
+
58
+ # Assistant chat (shared with the llm-chat-proxy Cloudflare Worker).
59
+ # These MUST match the worker's values byte-for-byte or every assistant
60
+ # message is rejected. Locally the worker reads them from its .dev.vars.
61
+ assistant_chat_jwt_secret: "some-assistant-chat-jwt-secret"
62
+ assistant_chat_hmac_secret: "some-assistant-chat-hmac-secret"
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.133.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