familia 2.0.0.pre2 → 2.0.0.pre4

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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +12 -5
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +3 -9
  5. data/lib/familia/core_ext.rb +2 -2
  6. data/lib/familia/features/expiration.rb +0 -1
  7. data/lib/familia/features/relatable_objects.rb +127 -0
  8. data/lib/familia/features.rb +7 -3
  9. data/lib/familia/horreum/class_methods.rb +32 -4
  10. data/lib/familia/secure_identifier.rb +129 -0
  11. data/lib/familia/utils.rb +7 -96
  12. data/lib/familia/version.rb +1 -1
  13. data/lib/familia.rb +2 -0
  14. data/try/configuration/scenarios_try.rb +43 -31
  15. data/try/core/errors_try.rb +10 -10
  16. data/try/core/extensions_try.rb +56 -23
  17. data/try/core/familia_extended_try.rb +3 -3
  18. data/try/core/familia_try.rb +0 -4
  19. data/try/core/middleware_try.rb +34 -40
  20. data/try/core/secure_identifier_try.rb +104 -0
  21. data/try/core/tools_try.rb +52 -36
  22. data/try/core/utils_try.rb +0 -98
  23. data/try/datatypes/boolean_try.rb +6 -7
  24. data/try/datatypes/datatype_base_try.rb +2 -2
  25. data/try/datatypes/hash_try.rb +0 -1
  26. data/try/datatypes/list_try.rb +0 -1
  27. data/try/datatypes/set_try.rb +0 -2
  28. data/try/datatypes/sorted_set_try.rb +1 -2
  29. data/try/datatypes/string_try.rb +1 -2
  30. data/try/edge_cases/empty_identifiers_try.rb +42 -35
  31. data/try/edge_cases/hash_symbolization_try.rb +5 -5
  32. data/try/edge_cases/json_serialization_try.rb +12 -13
  33. data/try/edge_cases/race_conditions_try.rb +46 -49
  34. data/try/edge_cases/reserved_keywords_try.rb +103 -49
  35. data/try/edge_cases/string_coercion_try.rb +2 -2
  36. data/try/edge_cases/ttl_side_effects_try.rb +44 -25
  37. data/try/features/expiration_try.rb +2 -2
  38. data/try/features/quantization_try.rb +2 -2
  39. data/try/features/relatable_objects_try.rb +221 -0
  40. data/try/features/safe_dump_advanced_try.rb +13 -14
  41. data/try/features/safe_dump_try.rb +8 -8
  42. data/try/helpers/test_helpers.rb +10 -12
  43. data/try/horreum/base_try.rb +9 -9
  44. data/try/horreum/class_methods_try.rb +27 -30
  45. data/try/horreum/commands_try.rb +69 -33
  46. data/try/horreum/initialization_try.rb +4 -4
  47. data/try/horreum/relations_try.rb +13 -14
  48. data/try/horreum/serialization_try.rb +3 -3
  49. data/try/horreum/settings_try.rb +25 -31
  50. data/try/integration/cross_component_try.rb +45 -35
  51. data/try/models/customer_safe_dump_try.rb +4 -4
  52. data/try/models/customer_try.rb +21 -24
  53. data/try/models/datatype_base_try.rb +0 -1
  54. data/try/models/familia_object_try.rb +3 -4
  55. data/try/performance/benchmarks_try.rb +47 -38
  56. data/try/prototypes/atomic_saves_v4.rb +3 -3
  57. metadata +15 -12
  58. data/try/core/refinements_try.rb +0 -39
  59. /data/try/{pooling/connection_pool_test_try.rb → core/pools_try.rb} +0 -0
  60. /data/try/{pooling → prototypes/pooling}/README.md +0 -0
  61. /data/try/{pooling/configurable_stress_test_try.rb → prototypes/pooling/configurable_stress_test.rb} +0 -0
  62. /data/try/{pooling → prototypes/pooling}/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
  63. /data/try/{pooling → prototypes/pooling}/lib/connection_pool_metrics.rb +0 -0
  64. /data/try/{pooling → prototypes/pooling}/lib/connection_pool_stress_test.rb +0 -0
  65. /data/try/{pooling → prototypes/pooling}/lib/connection_pool_threading_models.rb +0 -0
  66. /data/try/{pooling → prototypes/pooling}/lib/visualize_stress_results.rb +0 -0
  67. /data/try/{pooling/pool_siege_try.rb → prototypes/pooling/pool_siege.rb} +0 -0
  68. /data/try/{pooling/run_stress_tests_try.rb → prototypes/pooling/run_stress_tests.rb} +0 -0
@@ -7,104 +7,6 @@ Familia.debug = false
7
7
 
8
8
  # Test Familia utility methods and helpers
9
9
 
10
- ##
11
- ## ID Generation
12
- ##
13
-
14
- ## Familia.generate_id
15
- Familia.respond_to?(:generate_id)
16
- #=> true
17
-
18
- ## Can generate a default base-36 ID
19
- id = Familia.generate_id
20
- [id.class, id.length > 10, id.match?(/^[a-z0-9]+$/)]
21
- #=> [String, true, true]
22
-
23
- ## Generated IDs are unique
24
- [Familia.generate_id == Familia.generate_id]
25
- #=> [false]
26
-
27
- ## Can generate an ID with a custom base (hex)
28
- hex_id = Familia.generate_id(16)
29
- [hex_id.class, hex_id.length == 64, hex_id.match?(/^[a-f0-9]+$/)]
30
- #=> [String, true, true]
31
-
32
- ## Familia.generate_trace_id
33
- Familia.respond_to?(:generate_trace_id)
34
- #=> true
35
-
36
- ## Can generate a default base-36 trace ID
37
- trace_id = Familia.generate_trace_id
38
- [trace_id.class, trace_id.length > 5, trace_id.length < 20]
39
- #=> [String, true, true]
40
-
41
- ## Can generate a trace ID with a custom base (hex)
42
- hex_trace_id = Familia.generate_trace_id(16)
43
- [hex_trace_id.class, hex_trace_id.length == 16]
44
- #=> [String, true]
45
-
46
- ## Familia.generate_hex_id
47
- Familia.respond_to?(:generate_hex_id)
48
- #=> true
49
-
50
- ## Can generate a 256-bit hex ID
51
- hex_id = Familia.generate_hex_id
52
- [hex_id.class, hex_id.length == 64, hex_id.match?(/^[a-f0-9]+$/)]
53
- #=> [String, true, true]
54
-
55
- ## Familia.generate_hex_trace_id
56
- Familia.respond_to?(:generate_hex_trace_id)
57
- #=> true
58
-
59
- ## Can generate a 64-bit hex trace ID
60
- hex_trace_id = Familia.generate_hex_trace_id
61
- [hex_trace_id.class, hex_trace_id.length == 16, hex_trace_id.match?(/^[a-f0-9]+$/)]
62
- #=> [String, true, true]
63
-
64
-
65
- ##
66
- ## ID Shortening
67
- ##
68
-
69
- ## Familia.shorten_to_external_id
70
- Familia.respond_to?(:shorten_to_external_id)
71
- #=> true
72
-
73
- ## Can shorten hex ID to external ID (128 bits)
74
- hex_id = Familia.generate_hex_id
75
- external_id = Familia.shorten_to_external_id(hex_id)
76
- [external_id.class, external_id.length < hex_id.length]
77
- #=> [String, true]
78
-
79
- ## Can shorten hex ID to external ID with custom base (hex)
80
- hex_id = Familia.generate_hex_id
81
- hex_external_id = Familia.shorten_to_external_id(hex_id, base: 16)
82
- [hex_external_id.class, hex_external_id.length == 32]
83
- #=> [String, true]
84
-
85
- ## Familia.shorten_to_trace_id
86
- Familia.respond_to?(:shorten_to_trace_id)
87
- #=> true
88
-
89
- ## Can shorten hex ID to trace ID (64 bits)
90
- hex_id = Familia.generate_hex_id
91
- trace_id = Familia.shorten_to_trace_id(hex_id)
92
- [trace_id.class, trace_id.length < hex_id.length]
93
- #=> [String, true]
94
-
95
- ## Can shorten hex ID to trace ID with custom base (hex)
96
- hex_id = Familia.generate_hex_id
97
- hex_trace_id = Familia.shorten_to_trace_id(hex_id, base: 16)
98
- [hex_trace_id.class, hex_trace_id.length == 16]
99
- #=> [String, true]
100
-
101
- ## Shortened IDs are deterministic
102
- hex_id = Familia.generate_hex_id
103
- id1 = Familia.shorten_to_external_id(hex_id)
104
- id2 = Familia.shorten_to_external_id(hex_id)
105
- id1 == id2
106
- #=> true
107
-
108
10
  ##
109
11
  ## Key helpers
110
12
  ##
@@ -7,37 +7,36 @@ Familia.debug = false
7
7
 
8
8
  @hashkey = Familia::HashKey.new 'key'
9
9
 
10
-
11
10
  ## Boolean values are returned as strings, on assignment as string
12
- @hashkey["test"] = "true"
11
+ @hashkey['test'] = 'true'
13
12
  #=> "true"
14
13
 
15
14
  ## Boolean values are returned as strings
16
- @hashkey["test"]
15
+ @hashkey['test']
17
16
  #=> "true"
18
17
 
19
18
  ## Trying to store a boolean value to a hash key raises an exception
20
19
  begin
21
- @hashkey["test"] = true
20
+ @hashkey['test'] = true
22
21
  rescue Familia::HighRiskFactor => e
23
22
  e.message
24
23
  end
25
24
  #=> "High risk factor for serialization bugs: true<TrueClass>"
26
25
 
27
26
  ## Boolean values are returned as strings
28
- @hashkey["test"]
27
+ @hashkey['test']
29
28
  #=> "true"
30
29
 
31
30
  ## Trying to store a nil value to a hash key raises an exception
32
31
  begin
33
- @hashkey["test"] = nil
32
+ @hashkey['test'] = nil
34
33
  rescue Familia::HighRiskFactor => e
35
34
  e.message
36
35
  end
37
36
  #=> "High risk factor for serialization bugs: <NilClass>"
38
37
 
39
38
  ## The exceptions prevented the hash from being updated
40
- @hashkey["test"]
39
+ @hashkey['test']
41
40
  #=> "true"
42
41
 
43
42
  ## Clear the hash key
@@ -22,7 +22,7 @@ p [@a.name, @b.name]
22
22
  #=> true
23
23
 
24
24
  ## Limiter#qstamp
25
- @limiter1.counter.qstamp(10.minutes, '%H:%M', 1302468980)
25
+ @limiter1.counter.qstamp(10.minutes, '%H:%M', 1_302_468_980)
26
26
  ##=> '20:50'
27
27
 
28
28
  ## Database Types can be stored to quantized stamp suffix
@@ -33,7 +33,7 @@ p [@a.name, @b.name]
33
33
  @limiter2 = Limiter.new :requests
34
34
  p [@limiter1.default_expiration, @limiter2.default_expiration]
35
35
  p [@limiter1.counter.parent.default_expiration, @limiter2.counter.parent.default_expiration]
36
- @limiter2.counter.qstamp(10.minutes, pattern: nil, time: 1302468980)
36
+ @limiter2.counter.qstamp(10.minutes, pattern: nil, time: 1_302_468_980)
37
37
  #=> 1302468600
38
38
 
39
39
  ## Database Types can be stored to quantized numeric suffix. This
@@ -51,5 +51,4 @@ require_relative '../helpers/test_helpers'
51
51
  @a.props.values_at 'fieldA', 'counter', 'fieldC'
52
52
  #=> ['1', '40', '3']
53
53
 
54
-
55
54
  @a.props.delete!
@@ -1,6 +1,5 @@
1
1
  # try/datatypes/list_try.rb
2
2
 
3
-
4
3
  require_relative '../../lib/familia'
5
4
  require_relative '../helpers/test_helpers'
6
5
 
@@ -1,6 +1,5 @@
1
1
  # try/datatypes/set_try.rb
2
2
 
3
-
4
3
  require_relative '../../lib/familia'
5
4
  require_relative '../helpers/test_helpers'
6
5
 
@@ -32,5 +31,4 @@ ret.class
32
31
  @a.tags.size
33
32
  #=> 3
34
33
 
35
-
36
34
  @a.tags.delete!
@@ -3,7 +3,6 @@
3
3
  require_relative '../../lib/familia'
4
4
  require_relative '../helpers/test_helpers'
5
5
 
6
-
7
6
  @a = Bone.new 'atoken'
8
7
 
9
8
  ## Familia::SortedSet#add
@@ -36,7 +35,7 @@ require_relative '../helpers/test_helpers'
36
35
  #=> ['metric1', 'metric2', 'metric3']
37
36
 
38
37
  ## Familia::SortedSet#rangebyscore with a limit
39
- @a.metrics.rangebyscore 1, 3, :limit => [0, 2]
38
+ @a.metrics.rangebyscore 1, 3, limit: [0, 2]
40
39
  #=> ['metric1', 'metric2']
41
40
 
42
41
  ## Familia::SortedSet#increment
@@ -14,7 +14,7 @@ require_relative '../helpers/test_helpers'
14
14
  #=> 'GREAT!'
15
15
 
16
16
  ## Familia::String#value=
17
- @a.value.value = "DECENT!"
17
+ @a.value.value = 'DECENT!'
18
18
  #=> 'DECENT!'
19
19
 
20
20
  ## Familia::String#to_s
@@ -62,5 +62,4 @@ require_relative '../helpers/test_helpers'
62
62
  @ret.value
63
63
  #=> '1050bytes'
64
64
 
65
-
66
65
  @ret.delete!
@@ -1,48 +1,55 @@
1
- require_relative '../helpers/test_helpers'
1
+ # try/edge_cases/empty_identifiers_try.rb
2
2
 
3
3
  # Test empty identifier edge cases
4
- group "Empty Identifier Edge Cases"
5
4
 
6
- setup do
7
- @user_class = Class.new(Familia::Horreum) do
8
- identifier :email
9
- field :name
10
- end
11
- end
5
+ require_relative '../helpers/test_helpers'
12
6
 
13
- try "empty string identifier causes stack overflow" do
14
- user = @user_class.new(email: "", name: "Test")
15
7
 
16
- begin
17
- user.exists? # This should cause infinite loop
18
- false
19
- rescue SystemStackError
20
- true # Expected stack overflow
21
- rescue => e
22
- false # Unexpected error
8
+ ## empty string identifier handling
9
+ begin
10
+ user_class = Class.new(Familia::Horreum) do
11
+ identifier_field :email
12
+ field :email
13
+ field :name
23
14
  end
15
+ user = user_class.new(email: '', name: 'Test')
16
+ user.exists? # Test actual behavior with empty identifier
17
+ rescue SystemStackError
18
+ 'stack_overflow' # Stack overflow occurred
19
+ rescue StandardError => e
20
+ e.class.name # Other error occurred
24
21
  end
22
+ #=> "Familia::NoIdentifier"
25
23
 
26
- try "nil identifier causes stack overflow" do
27
- user = @user_class.new(email: nil, name: "Test")
28
-
29
- begin
30
- user.exists?
31
- false
32
- rescue SystemStackError, Familia::NoIdentifier
33
- true # Expected error
24
+ ## nil identifier handling
25
+ begin
26
+ user_class = Class.new(Familia::Horreum) do
27
+ identifier_field :email
28
+ field :email
29
+ field :name
34
30
  end
31
+ user = user_class.new(email: nil, name: 'Test')
32
+ user.exists?
33
+ rescue SystemStackError
34
+ 'stack_overflow'
35
+ rescue Familia::NoIdentifier => e
36
+ 'no_identifier'
37
+ rescue StandardError => e
38
+ e.class.name
35
39
  end
40
+ #=> "no_identifier"
36
41
 
37
- try "validation workaround prevents stack overflow" do
38
- user = @user_class.new(email: "", name: "Test")
39
-
40
- # Workaround: validate before operations
41
- if user.identifier.to_s.empty?
42
- raise ArgumentError, "Empty identifier"
42
+ ## empty identifier validation check
43
+ begin
44
+ user_class = Class.new(Familia::Horreum) do
45
+ identifier_field :email
46
+ field :email
47
+ field :name
43
48
  end
44
-
45
- false # Should not reach here
46
- rescue ArgumentError => e
47
- e.message.include?("Empty identifier")
49
+ user = user_class.new(email: '', name: 'Test')
50
+ # Check if identifier is empty
51
+ user.identifier.to_s.empty?
52
+ rescue StandardError => e
53
+ e.class.name
48
54
  end
55
+ #=> true
@@ -16,9 +16,9 @@ class SymbolizeTest < Familia::Horreum
16
16
  field :config
17
17
  end
18
18
 
19
- @test_hash = { "name" => "John", "age" => 30, "nested" => { "theme" => "dark" } }
19
+ @test_hash = { 'name' => 'John', 'age' => 30, 'nested' => { 'theme' => 'dark' } }
20
20
  @test_obj = SymbolizeTest.new
21
- @test_obj.id = "symbolize_test_1"
21
+ @test_obj.id = 'symbolize_test_1'
22
22
  @test_obj.config = @test_hash
23
23
  @test_obj.save
24
24
 
@@ -55,18 +55,18 @@ end
55
55
  #=> ["name", "age", "nested"]
56
56
 
57
57
  ## Nested hash in string result also has string keys
58
- @string_result["nested"].keys
58
+ @string_result['nested'].keys
59
59
  #=> ["theme"]
60
60
 
61
61
  ## Values are preserved correctly in both cases
62
62
  @symbol_result[:name]
63
63
  #=> "John"
64
64
 
65
- @string_result["name"]
65
+ @string_result['name']
66
66
  #=> "John"
67
67
 
68
68
  ## Arrays are handled correctly too
69
- @test_obj.config = [{ "item" => "value" }, "string", 123]
69
+ @test_obj.config = [{ 'item' => 'value' }, 'string', 123]
70
70
  @test_obj.save
71
71
  @array_json = @test_obj.hget('config')
72
72
  #=> "[{\"item\":\"value\"},\"string\",123]"
@@ -14,22 +14,21 @@ class JsonTest < Familia::Horreum
14
14
  field :simple # This should store simple strings as-is
15
15
  end
16
16
 
17
-
18
17
  ## Test 1: Store a Hash - should serialize to JSON automatically
19
18
  test_obj = JsonTest.new
20
- test_obj.config = { theme: "dark", notifications: true, settings: { volume: 80 } }
19
+ test_obj.config = { theme: 'dark', notifications: true, settings: { volume: 80 } }
21
20
  test_obj.config
22
21
  #=:> Hash
23
22
 
24
23
  ## Test 2: Store an Array - should serialize to JSON automatically
25
24
  test_obj = JsonTest.new
26
- test_obj.tags = ["ruby", "valkey", "json", "familia"]
25
+ test_obj.tags = %w[ruby valkey json familia]
27
26
  test_obj.tags
28
27
  #=:> Array
29
28
 
30
29
  ## Test 3: Store a simple string - should remain as string
31
30
  test_obj = JsonTest.new
32
- test_obj.simple = "just a string"
31
+ test_obj.simple = 'just a string'
33
32
  test_obj.simple
34
33
  #=:> String
35
34
 
@@ -40,25 +39,25 @@ test_obj.save
40
39
 
41
40
  ## Verify what's actually stored in Database (raw)
42
41
  test_obj = JsonTest.new
43
- test_obj.id = "json_test_1"
44
- test_obj.config = { theme: "dark", notifications: true, settings: { volume: 80 } }
45
- test_obj.simple = "just a string"
46
- test_obj.tags = ["ruby", "valkey", "json", "familia"]
42
+ test_obj.id = 'json_test_1'
43
+ test_obj.config = { theme: 'dark', notifications: true, settings: { volume: 80 } }
44
+ test_obj.simple = 'just a string'
45
+ test_obj.tags = %w[ruby valkey json familia]
47
46
  test_obj.save
48
47
  test_obj.hgetall
49
48
  #=> {"id"=>"json_test_1", "config"=>"{\"theme\":\"dark\",\"notifications\":true,\"settings\":{\"volume\":80}}", "tags"=>"[\"ruby\",\"valkey\",\"json\",\"familia\"]", "simple"=>"just a string"}
50
49
 
51
50
  ## Test 4: Hash should be deserialized back to Hash
52
51
  test_obj = JsonTest.new 'any_id_will_do'
53
- puts "Config after refresh:"
52
+ puts 'Config after refresh:'
54
53
  puts test_obj.config
55
- puts "Config class: "
54
+ puts 'Config class: '
56
55
  [test_obj.config.class, test_obj.config]
57
56
  ##=> [Hash, {:theme=>"dark", :notifications=>true, :settings=>{:volume=>80}}]
58
57
 
59
58
  ## Test 5: Array should be deserialized back to Array
60
59
  test_obj = JsonTest.new 'any_id_will_do'
61
- puts "Tags after refresh:"
60
+ puts 'Tags after refresh:'
62
61
  puts test_obj.tags.inspect
63
62
  puts "Tags class: #{test_obj.tags.class}"
64
63
  test_obj.tags.inspect
@@ -67,7 +66,7 @@ test_obj.tags
67
66
 
68
67
  ## Test 6: Simple string should remain a string (this works correctly)
69
68
  test_obj = JsonTest.new 'any_id_will_do'
70
- puts "Simple after refresh:"
69
+ puts 'Simple after refresh:'
71
70
  puts test_obj.simple.inspect
72
71
  puts "Simple class: #{test_obj.simple.class}"
73
72
  [test_obj.simple.class, test_obj.simple]
@@ -77,7 +76,7 @@ puts "Simple class: #{test_obj.simple.class}"
77
76
  test_obj = JsonTest.new 'any_id_will_do'
78
77
  puts "\n=== ASYMMETRY DEMONSTRATION ==="
79
78
  puts "Before save: config is #{test_obj.config.class}"
80
- test_obj.config = { example: "data" }
79
+ test_obj.config = { example: 'data' }
81
80
  puts "After assignment: config is #{test_obj.config.class}"
82
81
  test_obj.save
83
82
  puts "After save: config is still #{test_obj.config.class}"
@@ -1,60 +1,57 @@
1
- require_relative '../helpers/test_helpers'
2
-
3
1
  # Test connection race conditions
4
- group "Race Conditions Edge Cases"
5
2
 
6
- setup do
7
- @user_class = Class.new(Familia::Horreum) do
8
- identifier :email
9
- field :counter
10
- end
3
+ require_relative '../helpers/test_helpers'
4
+
5
+ ## concurrent connection access test
6
+ user_class = Class.new(Familia::Horreum) do
7
+ identifier_field :email
8
+ field :email
9
+ field :counter
11
10
  end
12
11
 
13
- try "concurrent connection access causes race condition" do
14
- user = @user_class.new(email: "test@example.com", counter: 0)
15
- user.save
16
-
17
- threads = []
18
- results = []
19
-
20
- # Simulate high concurrency
21
- 10.times do
22
- threads << Thread.new do
23
- begin
24
- user.incr(:counter)
25
- results << "success"
26
- rescue => e
27
- results << "error: #{e.class.name}"
28
- end
29
- end
30
- end
12
+ user = user_class.new(email: 'test@example.com', counter: 0)
13
+ user.save
31
14
 
32
- threads.each(&:join)
15
+ threads = []
16
+ results = []
33
17
 
34
- # May show race condition issues
35
- errors = results.count { |r| r.start_with?("error") }
36
- errors > 0 # Expects some race condition errors
37
- ensure
38
- user&.delete!
18
+ # Simulate high concurrency
19
+ 10.times do
20
+ threads << Thread.new do
21
+ user.incr(:counter)
22
+ results << 'success'
23
+ rescue StandardError => e
24
+ results << "error: #{e.class.name}"
25
+ end
39
26
  end
40
27
 
41
- try "connection pool stress test" do
42
- users = []
43
-
44
- # Create multiple users concurrently
45
- threads = []
46
- 20.times do |i|
47
- threads << Thread.new do
48
- user = @user_class.new(email: "user#{i}@example.com")
49
- user.save
50
- users << user
51
- end
28
+ threads.each(&:join)
29
+ user.delete!
30
+
31
+ # Count successful operations
32
+ successes = results.count { |r| r == 'success' }
33
+ successes > 0 # Should have some successes
34
+ #=!> StandardError
35
+
36
+ ## connection pool stress test
37
+ ## We're just checking whether it completes within a reasonable time frame.
38
+ ## If it does fail either bc of the duration or contention then it's a problem.
39
+ success_count = 0
40
+ threads = []
41
+ mutex = Mutex.new
42
+
43
+ # Test concurrent connections
44
+ 100.times do |i|
45
+ threads << Thread.new do
46
+ # Try to get a connection and perform an operation
47
+ dbclient = Familia.dbclient
48
+ dbclient.ping
49
+ success_count += 1
52
50
  end
51
+ end
53
52
 
54
- threads.each(&:join)
53
+ threads.each(&:join)
55
54
 
56
- # Check for connection issues
57
- users.length > 0 # Some should succeed despite race conditions
58
- ensure
59
- users.each(&:delete!) rescue nil
60
- end
55
+ # Should have some successful connections
56
+ success_count > 0
57
+ #=%> 220