lazy_init 0.1.1 → 0.2.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.
@@ -1,68 +1,5 @@
1
1
  require 'benchmark'
2
- require 'lazy_init'
3
-
4
-
5
- # LazyInit vs Manual Performance Benchmark
6
- # Testing with realistic production-like expensive operations:
7
-
8
- # --- Configuration Parsing ---
9
- # Description: JSON/YAML parsing with complex nested structures
10
-
11
- # Warming up (performing actual expensive computation)...
12
- # Initial computation time:
13
- # Manual: 14.25ms
14
- # LazyInit: 13.28ms
15
- # Difference: -0.96ms
16
-
17
- # Benchmarking cached access (100,000 iterations):
18
- # user system total real
19
- # Manual 0.012272 0.000034 0.012306 ( 0.012490)
20
- # LazyInit 0.041375 0.000231 0.041606 ( 0.041994)
21
-
22
- # ============================================================
23
-
24
- # --- Cryptographic Operations ---
25
- # Description: Secure token generation with multiple hash rounds
26
-
27
- # Warming up (performing actual expensive computation)...
28
- # Initial computation time:
29
- # Manual: 4.65ms
30
- # LazyInit: 2.37ms
31
- # Difference: -2.28ms
32
-
33
- # Benchmarking cached access (100,000 iterations):
34
- # user system total real
35
- # Manual 0.012351 0.000057 0.012408 ( 0.012445)
36
- # LazyInit 0.042226 0.000220 0.042446 ( 0.042752)
37
-
38
- # ============================================================
39
-
40
- # --- Data Processing ---
41
- # Description: ETL-style data aggregation and grouping
42
-
43
- # Warming up (performing actual expensive computation)...
44
- # Initial computation time:
45
- # Manual: 2.61ms
46
- # LazyInit: 2.84ms
47
- # Difference: 0.22ms
48
-
49
- # Benchmarking cached access (100,000 iterations):
50
- # user system total real
51
- # Manual 0.012233 0.000048 0.012281 ( 0.012375)
52
- # LazyInit 0.044622 0.000262 0.044884 ( 0.045389)
53
-
54
- # ============================================================
55
-
56
- # Thread Safety Test
57
- # Testing concurrent access to verify no race conditions...
58
- # Computing in thread: 60
59
- # Thread safety results:
60
- # Unique object IDs: 1 (should be 1)
61
- # All threads got same object: ✅ PASS
62
- # Total threads: 10
63
- # Test Environment: Ruby 3.0.2, x86_64
64
- # Platform: x86_64-darwin19
65
- #✅ Thread safety confirmed: All threads received the same computed object
2
+ require_relative '../lib/lazy_init.rb'
66
3
 
67
4
  module ExpensiveOperations
68
5
  def parse_configuration
@@ -244,7 +181,7 @@ puts "Test Environment: Ruby #{RUBY_VERSION}, #{RbConfig::CONFIG['target_cpu']}"
244
181
  puts "Platform: #{RUBY_PLATFORM}"
245
182
 
246
183
  if results.uniq.size == 1
247
- puts "\n Thread safety confirmed: All threads received the same computed object"
184
+ puts "\n Thread safety confirmed: All threads received the same computed object"
248
185
  else
249
- puts "\n Thread safety failed: Race condition detected!"
186
+ puts "\n Thread safety failed: Race condition detected!"
250
187
  end
@@ -114,60 +114,60 @@ module ProductionOperations
114
114
  host: 'prod-db.company.com',
115
115
  port: 5432,
116
116
  pool_size: 20,
117
- connections: Array.new(20) { |i| "conn_#{i}_#{rand(10000)}" }
117
+ connections: Array.new(20) { |i| "conn_#{i}_#{rand(10_000)}" }
118
118
  }
119
119
  end
120
-
120
+
121
121
  def create_connection_pool
122
122
  puts "Creating connection pool in thread: #{Thread.current.object_id}"
123
123
  sleep(0.2)
124
- pool_id = rand(100000)
124
+ pool_id = rand(100_000)
125
125
  {
126
126
  id: pool_id,
127
127
  connections: Array.new(50) { |i| "active_conn_#{pool_id}_#{i}" },
128
128
  created_at: Time.now
129
129
  }
130
130
  end
131
-
131
+
132
132
  def fetch_user_permissions
133
133
  puts "Fetching permissions in thread: #{Thread.current.object_id}"
134
134
  sleep(0.15)
135
135
  permissions = {}
136
- (1..1000).each { |i| permissions["user_#{i}"] = ["read", "write", "admin"].sample }
136
+ (1..1000).each { |i| permissions["user_#{i}"] = %w[read write admin].sample }
137
137
  permissions
138
138
  end
139
-
139
+
140
140
  def initialize_api_client
141
- puts "Initializing API client in thread: #{Thread.current.object_id}"
141
+ puts "Initializing API client in thread: #{Thread.current.object_id}"
142
142
  sleep(0.08)
143
143
  {
144
- client_id: "api_#{rand(50000)}",
145
- token: "bearer_#{rand(1000000)}",
144
+ client_id: "api_#{rand(50_000)}",
145
+ token: "bearer_#{rand(1_000_000)}",
146
146
  endpoints: %w[users orders payments analytics],
147
147
  initialized_at: Time.now
148
148
  }
149
149
  end
150
150
  end
151
151
 
152
- puts " === THREAD SAFETY TESTS ==="
152
+ puts ' === THREAD SAFETY TESTS ==='
153
153
  puts "Testing LazyInit in production-like concurrent scenarios\n\n"
154
154
 
155
- puts " TEST 1: Traffic Spike Simulation"
156
- puts "Simulating 200 simultaneous user requests hitting cached resources"
157
- puts "-" * 60
155
+ puts ' TEST 1: Traffic Spike Simulation'
156
+ puts 'Simulating 200 simultaneous user requests hitting cached resources'
157
+ puts '-' * 60
158
158
 
159
159
  class TrafficSpikeService
160
160
  extend LazyInit
161
161
  include ProductionOperations
162
-
162
+
163
163
  lazy_attr_reader :db_config do
164
164
  load_database_config
165
165
  end
166
-
166
+
167
167
  lazy_attr_reader :connection_pool do
168
- create_connection_pool
168
+ create_connection_pool
169
169
  end
170
-
170
+
171
171
  lazy_attr_reader :user_permissions do
172
172
  fetch_user_permissions
173
173
  end
@@ -177,26 +177,24 @@ spike_service = TrafficSpikeService.new
177
177
  spike_results = []
178
178
  spike_errors = []
179
179
 
180
- puts "Starting traffic spike test..."
180
+ puts 'Starting traffic spike test...'
181
181
  start_time = Time.now
182
182
 
183
183
  spike_threads = 200.times.map do |i|
184
184
  Thread.new do
185
- begin
186
- config = spike_service.db_config
187
- pool = spike_service.connection_pool
188
- permissions = spike_service.user_permissions
189
-
190
- spike_results << {
191
- thread: i,
192
- config_id: config.object_id,
193
- pool_id: pool.object_id,
194
- permissions_id: permissions.object_id,
195
- timestamp: Time.now
196
- }
197
- rescue => e
198
- spike_errors << { thread: i, error: e.message }
199
- end
185
+ config = spike_service.db_config
186
+ pool = spike_service.connection_pool
187
+ permissions = spike_service.user_permissions
188
+
189
+ spike_results << {
190
+ thread: i,
191
+ config_id: config.object_id,
192
+ pool_id: pool.object_id,
193
+ permissions_id: permissions.object_id,
194
+ timestamp: Time.now
195
+ }
196
+ rescue StandardError => e
197
+ spike_errors << { thread: i, error: e.message }
200
198
  end
201
199
  end
202
200
 
@@ -210,7 +208,7 @@ puts " Successful requests: #{spike_results.size}"
210
208
  puts " Errors: #{spike_errors.size}"
211
209
 
212
210
  config_ids = spike_results.map { |r| r[:config_id] }.uniq
213
- pool_ids = spike_results.map { |r| r[:pool_id] }.uniq
211
+ pool_ids = spike_results.map { |r| r[:pool_id] }.uniq
214
212
  permission_ids = spike_results.map { |r| r[:permissions_id] }.uniq
215
213
 
216
214
  puts "\n Race Condition Analysis:"
@@ -221,20 +219,20 @@ puts " Permissions - Unique objects: #{permission_ids.size} (should be 1)"
221
219
  spike_passed = config_ids.size == 1 && pool_ids.size == 1 && permission_ids.size == 1
222
220
  puts " Result: #{spike_passed ? '✅ PASS' : '❌ FAIL'} - No race conditions detected"
223
221
 
224
- puts "\n" + "="*60 + "\n"
222
+ puts "\n" + '=' * 60 + "\n"
225
223
 
226
- puts "TEST 2: Sustained Load Simulation"
227
- puts "Simulating background workers with sustained concurrent access"
228
- puts "-" * 60
224
+ puts 'TEST 2: Sustained Load Simulation'
225
+ puts 'Simulating background workers with sustained concurrent access'
226
+ puts '-' * 60
229
227
 
230
228
  class WorkerService
231
229
  extend LazyInit
232
230
  include ProductionOperations
233
-
231
+
234
232
  lazy_attr_reader :api_client do
235
233
  initialize_api_client
236
234
  end
237
-
235
+
238
236
  lazy_attr_reader :shared_cache do
239
237
  fetch_user_permissions
240
238
  end
@@ -244,29 +242,27 @@ worker_service = WorkerService.new
244
242
  worker_results = []
245
243
  worker_errors = []
246
244
 
247
- puts "Starting sustained load test (30 workers x 50 operations each)..."
245
+ puts 'Starting sustained load test (30 workers x 50 operations each)...'
248
246
 
249
247
  worker_threads = 30.times.map do |worker_id|
250
248
  Thread.new do
251
249
  thread_results = []
252
250
  50.times do |operation|
253
- begin
254
- api = worker_service.api_client
255
- cache = worker_service.shared_cache
256
-
257
- thread_results << {
258
- worker: worker_id,
259
- operation: operation,
260
- api_id: api.object_id,
261
- cache_id: cache.object_id
262
- }
263
-
264
- sleep(0.001) if operation % 10 == 0
265
- rescue => e
266
- worker_errors << { worker: worker_id, operation: operation, error: e.message }
267
- end
251
+ api = worker_service.api_client
252
+ cache = worker_service.shared_cache
253
+
254
+ thread_results << {
255
+ worker: worker_id,
256
+ operation: operation,
257
+ api_id: api.object_id,
258
+ cache_id: cache.object_id
259
+ }
260
+
261
+ sleep(0.001) if operation % 10 == 0
262
+ rescue StandardError => e
263
+ worker_errors << { worker: worker_id, operation: operation, error: e.message }
268
264
  end
269
-
265
+
270
266
  worker_results.concat(thread_results)
271
267
  end
272
268
  end
@@ -291,32 +287,32 @@ puts " Shared Cache - Unique objects: #{cache_ids.size} (should be 1)"
291
287
  worker_passed = api_ids.size == 1 && cache_ids.size == 1
292
288
  puts " Result: #{worker_passed ? '✅ PASS' : '❌ FAIL'} - Consistency maintained"
293
289
 
294
- puts "\n" + "="*60 + "\n"
290
+ puts "\n" + '=' * 60 + "\n"
295
291
 
296
- puts " TEST 3: Dependency Chain Stress Test"
297
- puts "Testing complex dependency resolution under concurrent pressure"
298
- puts "-" * 60
292
+ puts ' TEST 3: Dependency Chain Stress Test'
293
+ puts 'Testing complex dependency resolution under concurrent pressure'
294
+ puts '-' * 60
299
295
 
300
296
  class DependencyChainService
301
297
  extend LazyInit
302
298
  include ProductionOperations
303
-
299
+
304
300
  lazy_attr_reader :base_config do
305
301
  puts "Computing base_config in thread: #{Thread.current.object_id}"
306
302
  load_database_config
307
303
  end
308
-
304
+
309
305
  lazy_attr_reader :connection_manager, depends_on: [:base_config] do
310
306
  puts "Computing connection_manager in thread: #{Thread.current.object_id}"
311
307
  create_connection_pool
312
308
  end
313
-
309
+
314
310
  lazy_attr_reader :auth_service, depends_on: [:base_config] do
315
311
  puts "Computing auth_service in thread: #{Thread.current.object_id}"
316
312
  fetch_user_permissions
317
313
  end
318
-
319
- lazy_attr_reader :api_gateway, depends_on: [:connection_manager, :auth_service] do
314
+
315
+ lazy_attr_reader :api_gateway, depends_on: %i[connection_manager auth_service] do
320
316
  puts "Computing api_gateway in thread: #{Thread.current.object_id}"
321
317
  initialize_api_client
322
318
  end
@@ -325,12 +321,12 @@ end
325
321
  dependency_service = DependencyChainService.new
326
322
  dependency_results = []
327
323
 
328
- puts "Starting dependency chain test (100 concurrent accesses to complex dependency)..."
324
+ puts 'Starting dependency chain test (100 concurrent accesses to complex dependency)...'
329
325
 
330
326
  dependency_threads = 100.times.map do |i|
331
327
  Thread.new do
332
328
  gateway = dependency_service.api_gateway
333
-
329
+
334
330
  dependency_results << {
335
331
  thread: i,
336
332
  gateway_id: gateway.object_id,
@@ -356,16 +352,16 @@ puts " Unique gateway objects: #{gateway_ids.size} (should be 1)"
356
352
  dependency_passed = gateway_ids.size == 1
357
353
  puts " Result: #{dependency_passed ? '✅ PASS' : '❌ FAIL'} - Dependency resolution works correctly"
358
354
 
359
- puts "\n" + "="*60 + "\n"
355
+ puts "\n" + '=' * 60 + "\n"
360
356
 
361
- puts "TEST 4: Reset and Recovery Under Load"
362
- puts "Testing reset operations during concurrent access (production maintenance scenario)"
363
- puts "-" * 60
357
+ puts 'TEST 4: Reset and Recovery Under Load'
358
+ puts 'Testing reset operations during concurrent access (production maintenance scenario)'
359
+ puts '-' * 60
364
360
 
365
361
  class ResetTestService
366
362
  extend LazyInit
367
363
  include ProductionOperations
368
-
364
+
369
365
  lazy_attr_reader :service_config do
370
366
  load_database_config
371
367
  end
@@ -375,19 +371,17 @@ reset_service = ResetTestService.new
375
371
  reset_results = []
376
372
  reset_stats = { resets: 0, access_attempts: 0, successes: 0 }
377
373
 
378
- puts "Starting reset test (readers + periodic resets)..."
374
+ puts 'Starting reset test (readers + periodic resets)...'
379
375
 
380
376
  reader_threads = 20.times.map do |i|
381
377
  Thread.new do
382
378
  50.times do |attempt|
383
- begin
384
- reset_stats[:access_attempts] += 1
385
- config = reset_service.service_config
386
- reset_results << { thread: i, attempt: attempt, config_id: config.object_id }
387
- reset_stats[:successes] += 1
388
- sleep(0.01)
389
- rescue => e
390
- end
379
+ reset_stats[:access_attempts] += 1
380
+ config = reset_service.service_config
381
+ reset_results << { thread: i, attempt: attempt, config_id: config.object_id }
382
+ reset_stats[:successes] += 1
383
+ sleep(0.01)
384
+ rescue StandardError => e
391
385
  end
392
386
  end
393
387
  end
@@ -415,19 +409,19 @@ puts " Unique config objects: #{unique_configs.size} (should be > 1 due to rese
415
409
  reset_passed = unique_configs.size > 1 && reset_stats[:successes] > 0
416
410
  puts " Result: #{reset_passed ? '✅ PASS' : '❌ FAIL'} - Reset and recovery works correctly"
417
411
 
418
- puts "\n" + "="*60 + "\n"
412
+ puts "\n" + '=' * 60 + "\n"
419
413
 
420
414
  all_tests_passed = spike_passed && worker_passed && dependency_passed && reset_passed
421
415
 
422
- puts " === FINAL THREAD SAFETY SUMMARY ==="
416
+ puts ' === FINAL THREAD SAFETY SUMMARY ==='
423
417
  puts "Test Environment: Ruby #{RUBY_VERSION}, #{RbConfig::CONFIG['target_cpu']}"
424
418
  puts "Platform: #{RUBY_PLATFORM}"
425
- puts ""
426
- puts "Results:"
419
+ puts ''
420
+ puts 'Results:'
427
421
  puts " ✅ Traffic Spike (200 concurrent): #{spike_passed ? 'PASS' : 'FAIL'}"
428
- puts " ✅ Sustained Load (1500 operations): #{worker_passed ? 'PASS' : 'FAIL'}"
422
+ puts " ✅ Sustained Load (1500 operations): #{worker_passed ? 'PASS' : 'FAIL'}"
429
423
  puts " ✅ Dependency Chain (100 concurrent): #{dependency_passed ? 'PASS' : 'FAIL'}"
430
424
  puts " ✅ Reset Recovery (periodic resets): #{reset_passed ? 'PASS' : 'FAIL'}"
431
- puts ""
425
+ puts ''
432
426
  puts "Overall Result: #{all_tests_passed ? '✅ ALL TESTS PASSED' : '❌ SOME TESTS FAILED'}"
433
- puts ""
427
+ puts ''
data/lazy_init.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.metadata['source_code_uri'] = 'https://github.com/N3BCKN/lazy_init'
19
19
  spec.metadata['changelog_uri'] = 'https://github.com/N3BCKN/lazy_init/blob/main/CHANGELOG.md'
20
20
  spec.metadata['bug_tracker_uri'] = 'https://github.com/N3BCKN/lazy_init/issues'
21
- spec.metadata['documentation_uri'] = 'https://rubydoc.info/gems/lazy_init'
21
+ spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/lazy_init/#{LazyInit::VERSION}"
22
22
 
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
@@ -30,11 +30,11 @@ Gem::Specification.new do |spec|
30
30
  spec.require_paths = ['lib']
31
31
 
32
32
  spec.required_ruby_version = '>= 2.6'
33
-
33
+
34
34
  # Development dependencies
35
- spec.add_development_dependency 'rspec', '~> 3.12'
36
35
  spec.add_development_dependency 'benchmark-ips', '~> 2.10'
36
+ spec.add_development_dependency 'rake', '~> 13.0'
37
+ spec.add_development_dependency 'rspec', '~> 3.12'
37
38
  spec.add_development_dependency 'rubocop', '~> 1.50.2'
38
39
  spec.add_development_dependency 'yard', '~> 0.9'
39
- spec.add_development_dependency 'rake', '~> 13.0'
40
- end
40
+ end