placate 0.1.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 +7 -0
- data/.rubocop.yml +17 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +113 -0
- data/Rakefile +12 -0
- data/lib/placate/configuration.rb +11 -0
- data/lib/placate/unique_job.rb +47 -0
- data/lib/placate/version.rb +5 -0
- data/lib/placate.rb +26 -0
- data/sig/placate.rbs +4 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1c781f759fd2282b4cbbf32039ef53e5eed2f1959bf46d670b13cc57139e7a91
|
4
|
+
data.tar.gz: e96b43f21704a5ae648a1acc2a3dfd8530f5669c301cfdf872d44d8bbbc73510
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7d3005d9ff5ba7a53b501706066bb5d7657fa72e5e40b6846dab2e1b8282dcf973a5a932d4d1c932f02a2d2409851060b56c22d492b759d0915651dea7a60acf
|
7
|
+
data.tar.gz: e2be1bab70de91dd7e595c64c50367e3a2985551d69f2f587af4e0d3183c03c9ada2a3b99562497f2396f69609ee08d9e7f8922fcb7820f7663aa500dd2e6f7d
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.3
|
3
|
+
|
4
|
+
Style/StringLiterals:
|
5
|
+
Enabled: true
|
6
|
+
EnforcedStyle: double_quotes
|
7
|
+
|
8
|
+
Style/StringLiteralsInInterpolation:
|
9
|
+
Enabled: true
|
10
|
+
EnforcedStyle: double_quotes
|
11
|
+
|
12
|
+
Layout/LineLength:
|
13
|
+
Max: 120
|
14
|
+
|
15
|
+
Style/Documentation:
|
16
|
+
Description: Document classes and non-namespace modules.
|
17
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 TODO: Write your name
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# Placate
|
2
|
+
|
3
|
+
Placate is a simple Redis-based solution for preventing duplicate job execution in Ruby background job processors. It's designed to work with ActiveJob and any Redis-backed job system.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'placate'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
```bash
|
15
|
+
bundle install
|
16
|
+
```
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
### Basic Setup
|
21
|
+
|
22
|
+
First, configure Placate with your Redis connection:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
# config/initializers/placate.rb
|
26
|
+
Placate.configure do |config|
|
27
|
+
config.redis = Redis.new
|
28
|
+
config.default_lock_ttl = 30 # Default time-to-live in seconds
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
### In Your Jobs
|
33
|
+
|
34
|
+
Include the UniqueJob module in any job you want to prevent duplicates:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class ProcessOrderJob < ApplicationJob
|
38
|
+
include Placate::UniqueJob
|
39
|
+
|
40
|
+
def perform(order_id)
|
41
|
+
# Your job code here
|
42
|
+
end
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Now if you try to enqueue the same job with the same arguments within the TTL window, the duplicate will be prevented:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
ProcessOrderJob.perform_later(order_id: 123) # First job enqueued
|
50
|
+
ProcessOrderJob.perform_later(order_id: 123) # Blocked as duplicate
|
51
|
+
ProcessOrderJob.perform_later(order_id: 456) # Different args, will be enqueued
|
52
|
+
```
|
53
|
+
|
54
|
+
### Customization
|
55
|
+
|
56
|
+
You can customize the TTL per job class:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class LongRunningJob < ApplicationJob
|
60
|
+
include Placate::UniqueJob
|
61
|
+
|
62
|
+
self.unique_lock_ttl = 120 # 2 minutes
|
63
|
+
|
64
|
+
def perform
|
65
|
+
# Your job code here
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Or use a custom key prefix:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
class CustomPrefixJob < ApplicationJob
|
74
|
+
include Placate::UniqueJob
|
75
|
+
|
76
|
+
self.unique_lock_prefix = 'my_app_jobs'
|
77
|
+
|
78
|
+
def perform
|
79
|
+
# Your job code here
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
## How It Works
|
85
|
+
|
86
|
+
Placate uses Redis to maintain a lock based on the job class name and arguments. When a job is enqueued (`before_enqueue`):
|
87
|
+
|
88
|
+
1. A unique key is generated based on the job class and arguments
|
89
|
+
2. The current timestamp is stored in Redis with this key
|
90
|
+
3. If a key exists and its timestamp is within the TTL window, the job is not enqueued
|
91
|
+
4. If no key exists or the existing timestamp is older than the TTL, the job is enqueued
|
92
|
+
|
93
|
+
After the job is performed (`after_perform`):
|
94
|
+
|
95
|
+
1. The unique key is deleted, to enable the next job.
|
96
|
+
|
97
|
+
## Requirements
|
98
|
+
|
99
|
+
- Ruby 3.3+
|
100
|
+
- Rails 7.0+
|
101
|
+
- Redis
|
102
|
+
|
103
|
+
## Development
|
104
|
+
|
105
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
106
|
+
|
107
|
+
## Contributing
|
108
|
+
|
109
|
+
Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration.
|
110
|
+
|
111
|
+
## License
|
112
|
+
|
113
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest"
|
4
|
+
|
5
|
+
module Placate
|
6
|
+
module UniqueJob
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
def unique_lock_ttl
|
11
|
+
@unique_lock_ttl || Placate.configuration.default_lock_ttl
|
12
|
+
end
|
13
|
+
|
14
|
+
def unique_lock_ttl=(seconds)
|
15
|
+
@unique_lock_ttl = seconds
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
included do
|
20
|
+
class_attribute :unique_lock_prefix, instance_writer: false, default: nil
|
21
|
+
|
22
|
+
before_enqueue do |job|
|
23
|
+
args_hash = Digest::MD5.hexdigest(job.arguments.to_s)
|
24
|
+
prefix = job.class.unique_lock_prefix || job.class.name
|
25
|
+
lock_key = "#{prefix}:#{args_hash}:lock"
|
26
|
+
|
27
|
+
current_time = Time.now.to_i
|
28
|
+
existing_lock = Placate.redis.get(lock_key)
|
29
|
+
|
30
|
+
if existing_lock
|
31
|
+
lock_time = existing_lock.to_i
|
32
|
+
throw :abort if current_time - lock_time < (job.class.unique_lock_ttl || 30)
|
33
|
+
end
|
34
|
+
|
35
|
+
Placate.redis.set(lock_key, current_time)
|
36
|
+
end
|
37
|
+
|
38
|
+
after_perform do |job|
|
39
|
+
args_hash = Digest::MD5.hexdigest(job.arguments.to_s)
|
40
|
+
prefix = job.class.unique_lock_prefix || job.class.name
|
41
|
+
lock_key = "#{prefix}:#{args_hash}:lock"
|
42
|
+
|
43
|
+
Placate.redis.del(lock_key) if lock_key
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/placate.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
|
5
|
+
require_relative "placate/version"
|
6
|
+
require_relative "placate/configuration"
|
7
|
+
require_relative "placate/unique_job"
|
8
|
+
|
9
|
+
module Placate
|
10
|
+
class Error < StandardError; end
|
11
|
+
# Your code goes here...
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def configure
|
15
|
+
yield(configuration)
|
16
|
+
end
|
17
|
+
|
18
|
+
def configuration
|
19
|
+
@configuration ||= Configuration.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def redis
|
23
|
+
configuration.redis
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/sig/placate.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: placate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Toby
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-11-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activejob
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.16'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.21'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.21'
|
69
|
+
description: Prevents duplicate job execution using Redis locks for any Redis-based
|
70
|
+
job processing system
|
71
|
+
email:
|
72
|
+
- toby@darkroom.tech
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".rubocop.yml"
|
78
|
+
- CHANGELOG.md
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- lib/placate.rb
|
83
|
+
- lib/placate/configuration.rb
|
84
|
+
- lib/placate/unique_job.rb
|
85
|
+
- lib/placate/version.rb
|
86
|
+
- sig/placate.rbs
|
87
|
+
homepage: https://github.com/tobyond/placate
|
88
|
+
licenses:
|
89
|
+
- MIT
|
90
|
+
metadata:
|
91
|
+
homepage_uri: https://github.com/tobyond/placate
|
92
|
+
source_code_uri: https://github.com/tobyond/placate
|
93
|
+
changelog_uri: https://github.com/tobyond/placate/blob/main/CHANGELOG.md
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 3.3.0
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubygems_version: 3.5.3
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: Unique job handling for Redis-based job processors
|
113
|
+
test_files: []
|