You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implement a new aws_s3_stream output that provides true streaming S3 writes with proper partition routing, constant memory usage, and multipart upload support. This addresses critical memory scalability and data correctness issues in the existing aws_s3 output when writing partitioned data.
Motivation
The current aws_s3 output with batching has fundamental limitations that prevent it from being used in production for partitioned data pipelines:
1. Memory Scalability Issues
The batch approach buffers entire batches in memory before writing, causing memory usage to scale linearly with data volume:
Dataset Size
Batch Memory
Streaming Memory
Reduction
50K events
1.73 GB
0.17 GB
90%
100K events
3.31 GB
0.18 GB
95%
500K events
FAILED
0.17 GB
93%+
Impact:
Cannot process large datasets without massive memory allocation
Poor query performance (more S3 LIST operations, more file opens)
Data lake "small file problem" (Iceberg/Hive/Delta tables)
Higher S3 costs (more API calls)
Proposed Solution
Implement aws_s3_stream output that:
Streams data directly to S3 multipart uploads without buffering entire batches
Maintains separate writers per partition using partition_by parameter
Evaluates partition routing per-message instead of per-batch
Uses constant memory (~170-260 MB regardless of dataset size)
Design
Similar in approach to the aws_s3_parquet_stream implementation (PR #661), but adapted for generic text-based S3 writes:
output:
aws_s3_stream:
bucket: my-bucketregion: us-west-2path: 'logs/date=${! meta("date") }/account=${! meta("account") }/${! uuid_v4() }.json'# Partition routing - creates separate writer per partitionpartition_by:
- '${! meta("date") }'
- '${! meta("account") }'# Buffer settings - flushes when ANY condition metmax_buffer_bytes: 5242880# 5MB (S3 multipart optimal)max_buffer_count: 10000# Safety for tiny messagesmax_buffer_period: 10s# Low-volume partition flushing# Optional compression (applied in pipeline, not here)content_type: 'application/x-ndjson'content_encoding: 'gzip'# If using compress processor
Key Features
Per-partition writer pooling: Maintains active multipart upload per partition
Automatic buffer flushing: Triggers on size, count, or time (whichever comes first)
Graceful shutdown: Completes all multipart uploads on pipeline termination
S3 multipart optimization: 5MB parts for optimal throughput
Content type flexibility: Supports JSON, NDJSON, CSV, or any text format
Why No Built-in Compression?
Unlike aws_s3, this output does not provide a compress parameter. Compression should be handled in the pipeline using the compress processor instead:
Reason: S3 multipart uploads require non-final parts to be ≥5MB. After compressing a 5MB buffer, the compressed size is unpredictable (often <5MB), causing S3 to reject the upload. Handling this correctly would require complex logic to either:
Buffer much larger amounts (15-20MB+) uncompressed before compressing, negating memory benefits
Implement adaptive logic to skip compression on undersized parts, creating inconsistent compression within files
Fall back to uploading uncompressed data when compressed size is too small, adding complexity
Solution: Use pipeline-level compression which operates on messages before buffering:
pipeline:
processors:
- compress:
algorithm: gzipoutput:
aws_s3_stream:
content_encoding: 'gzip'# Set metadata for S3
This approach works reliably because concatenated gzip streams are valid (each compressed message can be decompressed independently). Testing shows 54% storage reduction with ~31% performance overhead.
Implementation
Core Components
output_aws_s3_stream.go: Main output plugin
Registers as aws_s3_stream output type
Manages writer pool (map of partition key → writer)
Summary
Implement a new
aws_s3_streamoutput that provides true streaming S3 writes with proper partition routing, constant memory usage, and multipart upload support. This addresses critical memory scalability and data correctness issues in the existingaws_s3output when writing partitioned data.Motivation
The current
aws_s3output withbatchinghas fundamental limitations that prevent it from being used in production for partitioned data pipelines:1. Memory Scalability Issues
The batch approach buffers entire batches in memory before writing, causing memory usage to scale linearly with data volume:
Impact:
2. Partition Routing Bug (Data Correctness)
The batch approach has a critical bug when batch boundaries cross partition boundaries - events are written to the wrong partitions:
Impact:
aws_s3_parquet_stream3. Performance Degradation at Scale
Batch performance becomes non-linear and eventually fails:
At small scales, batch is faster. At production scales, batch fails completely while streaming succeeds.
4. Small File Problem
Batch creates many small files instead of optimal large files per partition:
Impact:
Proposed Solution
Implement
aws_s3_streamoutput that:partition_byparameterDesign
Similar in approach to the
aws_s3_parquet_streamimplementation (PR #661), but adapted for generic text-based S3 writes:Key Features
Why No Built-in Compression?
Unlike
aws_s3, this output does not provide acompressparameter. Compression should be handled in the pipeline using thecompressprocessor instead:Reason: S3 multipart uploads require non-final parts to be ≥5MB. After compressing a 5MB buffer, the compressed size is unpredictable (often <5MB), causing S3 to reject the upload. Handling this correctly would require complex logic to either:
Solution: Use pipeline-level compression which operates on messages before buffering:
This approach works reliably because concatenated gzip streams are valid (each compressed message can be decompressed independently). Testing shows 54% storage reduction with ~31% performance overhead.
Implementation
Core Components
output_aws_s3_stream.go: Main output pluginaws_s3_streamoutput types3_streaming_writer.go: Generic S3 streaming writerConfiguration parameters:
aws_s3config)partition_by: Array of interpolated strings for partition keymax_buffer_bytes: Buffer size thresholdmax_buffer_count: Message count thresholdmax_buffer_period: Time thresholdPipeline Integration
Works seamlessly with existing pipeline processors:
Testing Results
Comprehensive testing with OCSF security event data (complex nested JSON):
Memory Efficiency
Partition Correctness
Performance
Compression Trade-offs (500K events)
Benefits
For Users
For Platform Teams
aws_s3For Data Lakes
Migration Path
Current (Broken)
Migrated (Fixed)
Key changes:
archive: lineslogic to pipeline (add\n)partition_byparameter for routingbatchingsection from outputaws_s3_streaminstead ofaws_s3Related Work
aws_s3_parquet_stream- Parallel PR solving same partition routing bug for Parquet formataws_s3_stream- Streaming approach for generic text formats (JSON, CSV, logs, etc.)