@@ -15,6 +15,8 @@ class KinesisBatchSender
1515 'ValidationException' ,
1616 ] . freeze
1717
18+ BATCH_TOO_LARGE_PATTERN = /too large/i
19+
1820 # Send a batch of database events to Kinesis
1921 #
2022 # Uses put_records batch API. Groups events by stream and sends each group as a batch.
@@ -40,11 +42,10 @@ def send_stream_batch(stream_name, stream_events)
4042 begin
4143 response = kinesis_client . put_records ( stream_name :, records :)
4244 process_response ( response , stream_events )
43- rescue Aws ::Kinesis ::Errors ::ValidationException
44- # Re-raise batch-level validation errors (configuration issues)
45- # These indicate invalid stream name, batch too large, etc.
46- # Not event data problems - requires manual intervention
47- raise
45+ rescue Aws ::Kinesis ::Errors ::ValidationException => e
46+ raise unless e . message . match? ( BATCH_TOO_LARGE_PATTERN )
47+
48+ handle_batch_too_large ( stream_name , stream_events )
4849 rescue StandardError => e
4950 # Handle transient errors (throttling, network issues, service unavailable)
5051 handle_transient_batch_error ( e , stream_events )
@@ -93,6 +94,36 @@ def create_failed_event(event, error_code:, error_message:, transient:)
9394 )
9495 end
9596
97+ def handle_batch_too_large ( stream_name , stream_events )
98+ if stream_events . size <= 1
99+ # Single event exceeds payload limit — treat as permanent failure
100+ return {
101+ succeeded : [ ] ,
102+ failed : stream_events . map do |event |
103+ create_failed_event (
104+ event ,
105+ error_code : 'ValidationException' ,
106+ error_message : 'Record exceeds Kinesis payload limit' ,
107+ transient : false ,
108+ )
109+ end ,
110+ }
111+ end
112+
113+ Rails . logger . warn (
114+ "[journaled] Batch too large for Kinesis (#{ stream_events . size } events), splitting in half and retrying" ,
115+ )
116+
117+ mid = stream_events . size / 2
118+ first_half = send_stream_batch ( stream_name , stream_events [ ...mid ] )
119+ second_half = send_stream_batch ( stream_name , stream_events [ mid ...] )
120+
121+ {
122+ succeeded : first_half [ :succeeded ] + second_half [ :succeeded ] ,
123+ failed : first_half [ :failed ] + second_half [ :failed ] ,
124+ }
125+ end
126+
96127 def handle_transient_batch_error ( error , stream_events )
97128 Rails . logger . error ( "Kinesis batch send failed (transient): #{ error . class } - #{ error . message } " )
98129
0 commit comments