solid_objects 0.14.2 → 0.14.4

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.
@@ -6,5 +6,38 @@ module SolidObjects
6
6
  def instrument(event, **payload, &block)
7
7
  ActiveSupport::Notifications.instrument("solid_objects.#{event}", payload, &block)
8
8
  end
9
+
10
+ # @rbs (Symbol, **untyped) -> void
11
+ def instrument_after_commit(event, **payload)
12
+ instrument(event, **payload)
13
+ rescue => error
14
+ report_instrumentation_failure(event, error)
15
+ end
16
+
17
+ private
18
+
19
+ # @rbs (Symbol, Exception) -> void
20
+ def report_instrumentation_failure(event, error)
21
+ instrument(
22
+ :"instrumentation.failed",
23
+ instrumentation_event: "solid_objects.#{event}",
24
+ error_class: error.class.name
25
+ )
26
+ rescue => failure
27
+ log_instrumentation_failure(event, failure)
28
+ end
29
+
30
+ # @rbs (Symbol, Exception) -> void
31
+ def log_instrumentation_failure(event, error)
32
+ SolidObjects.configuration.logger.error(
33
+ {
34
+ event: "solid_objects.instrumentation.failed",
35
+ instrumentation_event: "solid_objects.#{event}",
36
+ error_class: error.class.name
37
+ }
38
+ )
39
+ rescue
40
+ nil
41
+ end
9
42
  end
10
43
  end
@@ -4,17 +4,26 @@ module SolidObjects
4
4
  module Serialization
5
5
  MAX_NESTING = 100
6
6
 
7
+ Dumped = Data.define(:value, :byte_size)
8
+
7
9
  class << self
8
10
  # @rbs (untyped, ?max_bytes: Integer?) -> untyped
9
11
  def dump(value, max_bytes: nil)
12
+ return normalize(value) unless max_bytes
13
+
14
+ dump_with_byte_size(value, max_bytes:).value
15
+ end
16
+
17
+ # @rbs (untyped, ?max_bytes: Integer?) -> Dumped
18
+ def dump_with_byte_size(value, max_bytes: nil)
10
19
  normalized = normalize(value)
11
- encoded = JSON.generate(normalized, max_nesting: MAX_NESTING)
20
+ byte_size = JSON.generate(normalized, max_nesting: MAX_NESTING).bytesize
12
21
 
13
- if max_bytes && encoded.bytesize > max_bytes
22
+ if max_bytes && byte_size > max_bytes
14
23
  raise PayloadTooLarge, "serialized value exceeds #{max_bytes} bytes"
15
24
  end
16
25
 
17
- normalized
26
+ Dumped.new(value: normalized, byte_size:)
18
27
  rescue JSON::GeneratorError, EncodingError => error
19
28
  raise InvalidPayload, error.message
20
29
  end
@@ -28,7 +37,14 @@ module SolidObjects
28
37
 
29
38
  # @rbs (untyped) -> untyped
30
39
  def deep_copy(value)
31
- JSON.parse(JSON.generate(normalize(value), max_nesting: MAX_NESTING))
40
+ deep_copy_with_byte_size(value).value
41
+ end
42
+
43
+ # @rbs (untyped) -> Dumped
44
+ def deep_copy_with_byte_size(value)
45
+ encoded = JSON.generate(normalize(value), max_nesting: MAX_NESTING)
46
+
47
+ Dumped.new(value: JSON.parse(encoded), byte_size: encoded.bytesize)
32
48
  rescue JSON::GeneratorError, JSON::ParserError, EncodingError => error
33
49
  raise InvalidPayload, error.message
34
50
  end
@@ -60,7 +76,11 @@ module SolidObjects
60
76
  raise InvalidPayload, "serialized value is nested too deeply" if depth > MAX_NESTING
61
77
 
62
78
  case value
63
- when nil, true, false, String, Integer
79
+ when nil, true, false, Integer
80
+ value
81
+ when String
82
+ raise InvalidPayload, "strings must hold valid #{value.encoding} bytes" unless value.valid_encoding?
83
+
64
84
  value
65
85
  when Float
66
86
  raise InvalidPayload, "non-finite numbers are not supported" unless value.finite?
@@ -89,7 +109,7 @@ module SolidObjects
89
109
 
90
110
  # @rbs (untyped) -> String
91
111
  def normalize_key(key)
92
- return key if key.is_a?(String)
112
+ return normalize(key) if key.is_a?(String)
93
113
  return key.to_s if key.is_a?(Symbol)
94
114
 
95
115
  raise InvalidPayload, "JSON object keys must be strings or symbols"
@@ -52,6 +52,11 @@ module SolidObjects
52
52
  Serialization.deep_copy(data)
53
53
  end
54
54
 
55
+ # @rbs () -> Serialization::Dumped
56
+ def to_h_with_byte_size
57
+ Serialization.deep_copy_with_byte_size(data)
58
+ end
59
+
55
60
  # @rbs (Symbol | String) -> untyped
56
61
  def fetch(name)
57
62
  key = name.to_s
@@ -1,5 +1,5 @@
1
1
  # rbs_inline: enabled
2
2
 
3
3
  module SolidObjects
4
- VERSION = "0.14.2"
4
+ VERSION = "0.14.4"
5
5
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module SolidObjects
4
4
  class Configuration
5
- @table_name_prefix: String
5
+ @process_alive_threshold: Float
6
6
 
7
7
  @shutdown_timeout: Float
8
8
 
@@ -60,6 +60,8 @@ module SolidObjects
60
60
 
61
61
  @transmission_actor_type_resolver: Proc
62
62
 
63
+ @table_name_prefix: String
64
+
63
65
  @polling_interval: Float
64
66
 
65
67
  @idle_polling_interval: Float
@@ -84,6 +86,8 @@ module SolidObjects
84
86
 
85
87
  @max_state_bytes: Integer
86
88
 
89
+ @warn_state_bytes: Integer
90
+
87
91
  @max_result_bytes: Integer
88
92
 
89
93
  @max_attempts: Integer
@@ -94,8 +98,6 @@ module SolidObjects
94
98
 
95
99
  @process_heartbeat_interval: Float
96
100
 
97
- @process_alive_threshold: Float
98
-
99
101
  attr_accessor table_name_prefix: untyped
100
102
 
101
103
  attr_accessor polling_interval: untyped
@@ -122,6 +124,8 @@ module SolidObjects
122
124
 
123
125
  attr_accessor max_state_bytes: untyped
124
126
 
127
+ attr_accessor warn_state_bytes: untyped
128
+
125
129
  attr_accessor max_result_bytes: untyped
126
130
 
127
131
  attr_accessor max_attempts: untyped
@@ -24,14 +24,20 @@ module SolidObjects
24
24
  # @rbs (MessageContext) -> untyped
25
25
  def invoke_actor: (MessageContext) -> untyped
26
26
 
27
- # @rbs (Hash[String, untyped]) -> void
28
- def ensure_query_did_not_mutate_state!: (Hash[String, untyped]) -> void
27
+ # @rbs (Hash[String, untyped], Hash[String, untyped]) -> void
28
+ def ensure_query_did_not_mutate_state!: (Hash[String, untyped], Hash[String, untyped]) -> void
29
29
 
30
30
  # @rbs (Hash[String, untyped], Hash[String, untyped]) -> Hash[String, untyped]
31
31
  def changed_observables: (Hash[String, untyped], Hash[String, untyped]) -> Hash[String, untyped]
32
32
 
33
- # @rbs (untyped, Hash[String, untyped], state_changed: bool) -> void
34
- def complete: (untyped, Hash[String, untyped], state_changed: bool) -> void
33
+ # @rbs (untyped, Hash[String, untyped], state_after: Serialization::Dumped, state_changed: bool) -> void
34
+ def complete: (untyped, Hash[String, untyped], state_after: Serialization::Dumped, state_changed: bool) -> void
35
+
36
+ # @rbs (Integer) -> void
37
+ def ensure_state_fits!: (Integer) -> void
38
+
39
+ # @rbs (Integer) -> void
40
+ def report_large_state: (Integer) -> void
35
41
 
36
42
  # @rbs (Array[Actor::CommitActionIntent]) -> void
37
43
  def execute_commit_actions: (Array[Actor::CommitActionIntent]) -> void
@@ -4,5 +4,16 @@ module SolidObjects
4
4
  module Instrumentation
5
5
  # @rbs (Symbol, **untyped) { (Hash[Symbol, untyped]) -> untyped } -> untyped
6
6
  def instrument: (Symbol, **untyped) { (Hash[Symbol, untyped]) -> untyped } -> untyped
7
+
8
+ # @rbs (Symbol, **untyped) -> void
9
+ def instrument_after_commit: (Symbol, **untyped) -> void
10
+
11
+ private
12
+
13
+ # @rbs (Symbol, Exception) -> void
14
+ def report_instrumentation_failure: (Symbol, Exception) -> void
15
+
16
+ # @rbs (Symbol, Exception) -> void
17
+ def log_instrumentation_failure: (Symbol, Exception) -> void
7
18
  end
8
19
  end
@@ -4,15 +4,34 @@ module SolidObjects
4
4
  module Serialization
5
5
  MAX_NESTING: ::Integer
6
6
 
7
+ class Dumped < Data
8
+ attr_reader value(): untyped
9
+
10
+ attr_reader byte_size(): untyped
11
+
12
+ def self.new: (untyped value, untyped byte_size) -> instance
13
+ | (value: untyped, byte_size: untyped) -> instance
14
+
15
+ def self.members: () -> [ :value, :byte_size ]
16
+
17
+ def members: () -> [ :value, :byte_size ]
18
+ end
19
+
7
20
  # @rbs (untyped, ?max_bytes: Integer?) -> untyped
8
21
  def self.dump: (untyped, ?max_bytes: Integer?) -> untyped
9
22
 
23
+ # @rbs (untyped, ?max_bytes: Integer?) -> Dumped
24
+ def self.dump_with_byte_size: (untyped, ?max_bytes: Integer?) -> Dumped
25
+
10
26
  # @rbs (untyped) -> untyped
11
27
  def self.load: (untyped) -> untyped
12
28
 
13
29
  # @rbs (untyped) -> untyped
14
30
  def self.deep_copy: (untyped) -> untyped
15
31
 
32
+ # @rbs (untyped) -> Dumped
33
+ def self.deep_copy_with_byte_size: (untyped) -> Dumped
34
+
16
35
  # @rbs (untyped) -> untyped
17
36
  def self.readonly_copy: (untyped) -> untyped
18
37
 
@@ -45,6 +45,9 @@ module SolidObjects
45
45
  # @rbs () -> Hash[String, untyped]
46
46
  def to_h: () -> Hash[String, untyped]
47
47
 
48
+ # @rbs () -> Serialization::Dumped
49
+ def to_h_with_byte_size: () -> Serialization::Dumped
50
+
48
51
  # @rbs (Symbol | String) -> untyped
49
52
  def fetch: (Symbol | String) -> untyped
50
53
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_objects
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.2
4
+ version: 0.14.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Carlson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-25 00:00:00.000000000 Z
11
+ date: 2026-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actioncable
@@ -323,6 +323,7 @@ files:
323
323
  - benchmark/idle_polling.rb
324
324
  - benchmark/processing.rb
325
325
  - benchmark/query_count.rb
326
+ - benchmark/state_size.rb
326
327
  - benchmark/support.rb
327
328
  - benchmark/sync_latency.rb
328
329
  - config/routes.rb