activejob-compressible 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 28d3f54a6ca82b0bfbc6d00db5ff31159f7ce19fd56e157e2805d913cec79165
4
+ data.tar.gz: dd256e7a07ff11bdb73180d05a4f85c1bfb94aab754b7c8d64ab22517aa95bb0
5
+ SHA512:
6
+ metadata.gz: d716916f8c7d9d4d8ee07a0ac08caedc9445e9cd6f8a0cd8946f1cc8f635b47fe87b0faa9a9f8c40953061d0bc035a9e0dcb1f0d53b7e444ebc17644c98faf26
7
+ data.tar.gz: ed162b58f90dbc68136bd2b5e6132ad4d64afa9d2291904733dd86baa1b87d94c4536334dbaa9822f1d0dffe1a093875d8dc1cb28aa200c476d54a61416257fe
data/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2025-06-05
9
+
10
+ ### Added
11
+ - Initial release of ActiveJob::Compressible gem
12
+ - Transparent compression/decompression for large ActiveJob payloads
13
+ - Automatic detection of Rails cache backend limits for smart defaults
14
+ - Configurable compression threshold via `ActiveJob::Compressible.configure`
15
+ - Support for zlib compression algorithm with base64 encoding
16
+ - Backward compatibility for existing uncompressed jobs
17
+ - Safe compression markers (`"_compressed"` key) for reliable detection
18
+ - Comprehensive test suite with dynamic configuration testing
19
+ - Support for Ruby >= 3.1 and Rails 6.0-8.x
20
+ - Detailed documentation and usage examples
21
+
22
+ ### Technical Details
23
+ - Uses zlib compression with base64 encoding for safety
24
+ - Only compresses Hash arguments that exceed the configured threshold
25
+ - Defaults to cache `value_max_bytes` minus 100KB for safety margin
26
+ - Graceful fallback to 948KB default if cache limits not detected
27
+ - Thread-safe configuration system
28
+ - Minimal performance overhead (~1-5ms for typical payloads)
29
+
30
+ ### Documentation
31
+ - Complete README with usage examples and configuration options
32
+ - API documentation with inline comments
33
+ - Development setup and testing instructions
34
+ - Performance characteristics and compatibility notes
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Evan Lee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # ActiveJob::Compressible
2
+
3
+ Transparent compression for large ActiveJob payloads to avoid cache backend limits.
4
+
5
+ ## Overview
6
+
7
+ The `ActiveJob::Compressible` concern provides automatic compression and decompression for large job arguments to prevent exceeding cache or queue backend size limits (such as Redis/Memcached). This is especially useful for jobs that handle large payloads like webhook events, API responses, or bulk data processing.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'activejob-compressible'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle install
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install activejob-compressible
24
+
25
+ ## Usage
26
+
27
+ Include the concern in your job classes:
28
+
29
+ ```ruby
30
+ class WebhookJob < ApplicationJob
31
+ include ActiveJob::Compressible
32
+
33
+ def perform(large_webhook_payload)
34
+ # Process the payload - compression/decompression is transparent
35
+ process_webhook(large_webhook_payload)
36
+ end
37
+ end
38
+ ```
39
+
40
+ ## How It Works
41
+
42
+ - **Automatic Compression**: When jobs are serialized (enqueued), the first argument is checked. If it's a Hash and its JSON representation exceeds the compression threshold, it's automatically compressed using zlib and base64 encoding.
43
+
44
+ - **Transparent Decompression**: When jobs are deserialized (dequeued), compressed arguments are automatically detected and decompressed.
45
+
46
+ - **Backward Compatibility**: Old jobs (created before adding the concern) continue to work without any changes during rolling deployments.
47
+
48
+ - **Safe Markers**: Compressed payloads are marked with a `"_compressed"` key to ensure reliable detection.
49
+
50
+ ## Configuration
51
+
52
+ ```ruby
53
+ ActiveJob::Compressible.configure do |config|
54
+ config.compression_threshold = 500_000 # Compress payloads > 500KB
55
+ end
56
+ ```
57
+
58
+ ### Configuration Options
59
+
60
+ - **`compression_threshold`**: Size in bytes above which payloads will be compressed. Defaults to your cache backend's `value_max_bytes` minus 100KB for safety, or 948KB if not detected.
61
+
62
+ - **`compression_algorithm`**: Currently only `:zlib` is supported.
63
+
64
+ ### Default Behavior
65
+
66
+ The gem automatically detects your Rails cache configuration and sets a safe default threshold:
67
+
68
+ ```ruby
69
+ # Automatically uses your cache backend's limits
70
+ max_bytes = Rails.cache.options[:value_max_bytes] || 1_048_576
71
+ default_threshold = max_bytes - 100_000 # Safe margin
72
+ ```
73
+
74
+ ## Examples
75
+
76
+ ### Basic Usage
77
+
78
+ ```ruby
79
+ class DataProcessingJob < ApplicationJob
80
+ include ActiveJob::Compressible
81
+
82
+ def perform(large_dataset)
83
+ # large_dataset is automatically compressed if > threshold
84
+ process_data(large_dataset)
85
+ end
86
+ end
87
+
88
+ # Enqueue with large payload
89
+ DataProcessingJob.perform_later({
90
+ "records" => large_array_of_data,
91
+ "metadata" => additional_info
92
+ })
93
+ ```
94
+
95
+ ### Custom Configuration
96
+
97
+ ```ruby
98
+ # config/initializers/activejob_compressible.rb
99
+ ActiveJob::Compressible.configure do |config|
100
+ config.compression_threshold = 750_000 # 750KB threshold
101
+ end
102
+ ```
103
+
104
+ ### Multiple Job Classes
105
+
106
+ ```ruby
107
+ class ApiResponseJob < ApplicationJob
108
+ include ActiveJob::Compressible
109
+ # Inherits default configuration
110
+ end
111
+
112
+ class BulkImportJob < ApplicationJob
113
+ include ActiveJob::Compressible
114
+ # Also inherits default configuration
115
+ end
116
+ ```
117
+
118
+ ## Requirements
119
+
120
+ - Ruby >= 2.7.0
121
+ - ActiveJob >= 6.0
122
+ - ActiveSupport >= 6.0
123
+
124
+ ## Compatibility
125
+
126
+ - **Rails 6.0-8.x**: Full support
127
+ - **Queue Adapters**: Works with any ActiveJob queue adapter (Sidekiq, Resque, etc.)
128
+ - **Cache Backends**: Automatically detects Redis, Memcached, and other cache limits
129
+ - **Rolling Deployments**: Safe to deploy incrementally
130
+
131
+ ## Performance
132
+
133
+ - **Compression Ratio**: Typically 60-90% size reduction for JSON payloads
134
+ - **Speed**: zlib compression/decompression adds ~1-5ms for typical payloads
135
+ - **Memory**: Minimal overhead, only processes large payloads
136
+
137
+ ## Development
138
+
139
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
140
+
141
+ ```bash
142
+ # Run tests
143
+ bundle exec rake test
144
+
145
+ # Run tests quietly
146
+ bundle exec rake test TESTOPTS="--quiet"
147
+
148
+ # Run RuboCop
149
+ bundle exec rubocop
150
+ ```
151
+
152
+ ## Contributing
153
+
154
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Archetypically/activejob-compressible.
155
+
156
+ ## License
157
+
158
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ # Compressible module provides transparent compression for large ActiveJob payloads.
5
+ module Compressible
6
+ # Configuration class for ActiveJob::Compressible settings.
7
+ #
8
+ # Manages compression behavior including threshold limits and algorithm selection.
9
+ # The compression threshold determines when job arguments should be compressed
10
+ # based on their serialized size.
11
+ class Configuration
12
+ attr_accessor :compression_threshold
13
+ attr_reader :compression_algorithm
14
+
15
+ def initialize
16
+ @compression_threshold = default_compression_threshold
17
+ @compression_algorithm = :zlib
18
+ end
19
+
20
+ def compression_algorithm=(algorithm)
21
+ raise ArgumentError, "Only :zlib compression algorithm is currently supported" unless algorithm == :zlib
22
+
23
+ @compression_algorithm = algorithm
24
+ end
25
+
26
+ private
27
+
28
+ def default_compression_threshold
29
+ max_bytes = begin
30
+ Rails.cache.options[:value_max_bytes] if defined?(Rails)
31
+ rescue StandardError
32
+ nil
33
+ end || 1_048_576
34
+ max_bytes - 100_000
35
+ end
36
+ end
37
+
38
+ def self.configuration
39
+ @configuration ||= Configuration.new
40
+ end
41
+
42
+ def self.configure
43
+ yield(configuration)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ module Compressible
5
+ VERSION = "1.0.0"
6
+ end
7
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "zlib"
5
+ require "base64"
6
+ require "json"
7
+
8
+ module ActiveJob
9
+ # CompressibleJob is a concern for ActiveJob classes that transparently compresses
10
+ # and decompresses large job arguments to avoid exceeding cache or queue backend limits
11
+ # (such as Dalli/Memcached or Redis). This is especially useful for jobs that handle
12
+ # large payloads, such as webhook events or API responses.
13
+ #
14
+ # Usage:
15
+ # class MyJob < ApplicationJob
16
+ # include ActiveJob::Compressible
17
+ # # ...
18
+ # end
19
+ #
20
+ # How it works:
21
+ # - When the job is serialized (enqueued), the first argument is checked.
22
+ # - If the argument is a Hash and its JSON representation exceeds the compression threshold,
23
+ # it is compressed using zlib and base64, and marked with a special key.
24
+ # - When the job is deserialized (dequeued), the argument is checked for the marker and
25
+ # transparently decompressed if needed.
26
+ # - Old jobs (with uncompressed payloads) are still supported for backward compatibility.
27
+ #
28
+ # Configuration:
29
+ # - The compression threshold is configurable via ActiveJob::Compressible.configuration
30
+ # - Defaults to cache backend's :value_max_bytes minus 100,000 bytes for safety
31
+ #
32
+ # This concern is safe for rolling deployments and can be included in any job class that may
33
+ # enqueue large payloads.
34
+ module Compressible
35
+ extend ActiveSupport::Concern
36
+
37
+ included do
38
+ def serialize
39
+ super.tap do |h|
40
+ if h["arguments"] && h["arguments"].first.is_a?(Hash)
41
+ h["arguments"][0] = self.class.compress_if_needed(h["arguments"].first)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ class_methods do
48
+ def deserialize(job_data)
49
+ arg = job_data["arguments"].first
50
+ job_data = job_data.dup
51
+ job_data["arguments"][0] = decompress_if_needed(arg)
52
+ super
53
+ end
54
+
55
+ def compress_if_needed(obj)
56
+ json_str = obj.to_json
57
+ if json_str.bytesize > compression_threshold
58
+ compressed = Base64.encode64(Zlib::Deflate.deflate(json_str))
59
+ { "_compressed" => true, "data" => compressed }
60
+ else
61
+ obj
62
+ end
63
+ end
64
+
65
+ def decompress_if_needed(arg)
66
+ if arg.is_a?(Hash) && arg["_compressed"]
67
+ JSON.parse(Zlib::Inflate.inflate(Base64.decode64(arg["data"])))
68
+ else
69
+ arg
70
+ end
71
+ end
72
+
73
+ def compression_threshold
74
+ ActiveJob::Compressible.configuration.compression_threshold
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "activejob/compressible/version"
4
+ require "activejob/compressible/configuration"
5
+ require "activejob/compressible"
@@ -0,0 +1,16 @@
1
+ module ActiveJob
2
+ module Compressible
3
+ class Configuration
4
+ attr_accessor compression_threshold: Integer
5
+ attr_reader compression_algorithm: Symbol
6
+
7
+ def initialize: () -> void
8
+
9
+ def compression_algorithm=: (Symbol algorithm) -> Symbol
10
+
11
+ private
12
+
13
+ def default_compression_threshold: () -> Integer
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveJob
2
+ module Compressible
3
+ VERSION: String
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveJob
2
+ module Compressible
3
+ extend ActiveSupport::Concern
4
+
5
+ def serialize: () -> Hash[String, untyped]
6
+
7
+ module ClassMethods
8
+ def deserialize: (Hash[String, untyped] job_data) -> ActiveJob::Base
9
+
10
+ def compress_if_needed: (Hash[String, untyped] obj) -> (Hash[String, untyped] | Hash[String, String | bool])
11
+
12
+ def decompress_if_needed: (untyped arg) -> untyped
13
+
14
+ def compression_threshold: () -> Integer
15
+ end
16
+
17
+ VERSION: String
18
+
19
+ def self.configuration: () -> Configuration
20
+
21
+ def self.configure: () { (Configuration) -> void } -> void
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activejob-compressible
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Evan Lee
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-06-05 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activejob
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '6.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '6.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: activesupport
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '6.0'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '9.0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '6.0'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '9.0'
52
+ description: Automatically compress/decompress large job arguments to avoid size restrictions
53
+ email:
54
+ - evan.lee@shopify.com
55
+ executables: []
56
+ extensions: []
57
+ extra_rdoc_files: []
58
+ files:
59
+ - CHANGELOG.md
60
+ - LICENSE
61
+ - README.md
62
+ - lib/activejob-compressible.rb
63
+ - lib/activejob/compressible.rb
64
+ - lib/activejob/compressible/configuration.rb
65
+ - lib/activejob/compressible/version.rb
66
+ - sig/activejob/compressible.rbs
67
+ - sig/activejob/compressible/configuration.rbs
68
+ - sig/activejob/compressible/version.rbs
69
+ homepage: https://github.com/Archetypically/activejob-compressible
70
+ licenses:
71
+ - MIT
72
+ metadata:
73
+ rubygems_mfa_required: 'true'
74
+ homepage_uri: https://github.com/Archetypically/activejob-compressible
75
+ source_code_uri: https://github.com/Archetypically/activejob-compressible
76
+ changelog_uri: https://github.com/Archetypically/activejob-compressible/blob/main/CHANGELOG.md
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '3.1'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.6.2
92
+ specification_version: 4
93
+ summary: Transparent compression for large ActiveJob payloads
94
+ test_files: []