status_workflow 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f0b810a0de956ac985342e3f395cd3ca3370a023
4
- data.tar.gz: c2cd573069b22280b2073088f71ed42489fa6897
3
+ metadata.gz: 31fac79b181eaf226d6f199406c58b23f2860674
4
+ data.tar.gz: abb35fc730390397b21692c0a0f398a9628253e1
5
5
  SHA512:
6
- metadata.gz: cf15a846af4ca562412ef389e9383bff8132d302c60fdb2d255aad602dba1692b061e4745fa70e562739c5d4efc9f3d65fe929d5ce014b02e7c9aa12fff86ac2
7
- data.tar.gz: 0ffcb10c6b5a145d7c1cd9806f3808317a69091b988ee6c2bb7f225b75d04aac828dad8bb641157001c7d4013132c53d7f29af3104370c05116e8fcf7110a705
6
+ metadata.gz: e7f31170e7845fcd47a2f0f9ab5e68c08b6a7eae0226f784b8060d563a4ffa812ca21d7ff79230e248fb899d2a325d57299ce830ade5ffb4776d412247889a85
7
+ data.tar.gz: 271e5943314d59d02ca2a8e48e2cd5750077fbea2986605c2ca3d0074de16efca5bbb407cbd5fc76b6d1316bb4a80629c8ddff2d877b574a9d9e7da5c1b9f50c
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+ *.gem
data/.travis.yml CHANGED
@@ -4,3 +4,4 @@ rvm:
4
4
  - 2.5
5
5
  services:
6
6
  - redis-server
7
+ - postgresql
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 1.0.1 / 2018-09-28
2
+
3
+ Enhancements
4
+
5
+ * Allow passing a block to be executed before the state transition
6
+
1
7
  1.0.0 / 2018-09-23
2
8
 
3
9
  Initial extraction from Faraday production systems
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # StatusWorkflow
2
2
 
3
+ [![Build Status](https://travis-ci.org/faradayio/status_workflow_ruby.svg?branch=master)](https://travis-ci.org/faradayio/status_workflow_ruby)
4
+
3
5
  Basic state machine using Redis for locking.
4
6
 
5
7
  ```
@@ -1,5 +1,6 @@
1
1
  require 'status_workflow/version'
2
2
  require 'timeout'
3
+ require 'set'
3
4
 
4
5
  module StatusWorkflow
5
6
  class InvalidTransition < StandardError; end
@@ -16,29 +17,57 @@ module StatusWorkflow
16
17
  @redis or raise("please set StatusWorkflow.redis=")
17
18
  end
18
19
 
20
+ LOCK_ACQUISITION_TIMEOUT = 8
21
+ LOCK_EXPIRY = 4
22
+ LOCK_CHECK_RATE = 0.2
23
+
19
24
  module ClassMethods
20
25
  def status_workflow(transitions)
21
26
  transitions.inject({}) do |memo, (from_status, to_statuses)|
22
27
  to_statuses.each do |to_status|
23
- memo[to_status] ||= []
28
+ memo[to_status] ||= Set.new
24
29
  memo[to_status] << from_status
25
30
  end
26
31
  memo
27
32
  end.each do |to_status, from_statuses|
28
- define_method "enter_#{to_status}!" do
33
+ define_method "enter_#{to_status}!" do |&blk|
29
34
  lock_key = "status_workflow/#{self.class.name}/#{id}"
30
- Timeout.timeout(8, nil, "timeout waiting for #{self.class.name}/#{id} lock") do
31
- until StatusWorkflow.redis.set(lock_key, true, nx: true, ex: 4)
32
- sleep 0.2
35
+ # Give ourselves 8 seconds to get the lock, checking every 0.2 seconds
36
+ Timeout.timeout(LOCK_ACQUISITION_TIMEOUT, nil, "timeout waiting for #{self.class.name}/#{id} lock") do
37
+ until StatusWorkflow.redis.set(lock_key, true, nx: true, ex: LOCK_EXPIRY)
38
+ sleep LOCK_CHECK_RATE
33
39
  end
34
40
  end
35
- # got the lock, i have 3 (4 expiry on lock - 1 for safety) seconds to set it
36
- Timeout.timeout(3, nil, "timeout waiting for #{self.class.name}/#{id} status update") do
37
- # depend on #can_enter_X to reload
38
- raise InvalidTransition, "can't enter #{to_status} from #{status}, expected #{from_statuses.join('/')}" unless send("can_enter_#{to_status}?")
41
+ heartbeat = nil
42
+ begin
43
+ # Give ourselves 2 seconds to check the status of the lock
44
+ Timeout.timeout(2, nil, "timeout waiting for #{self.class.name}/#{id} status check") do
45
+ # depend on #can_enter_X to reload
46
+ raise InvalidTransition, "can't enter #{to_status} from #{status}, expected #{from_statuses.to_a.join('/')}" unless send("can_enter_#{to_status}?")
47
+ end
48
+ # If a block was given, start a heartbeat thread
49
+ if blk
50
+ begin
51
+ heartbeat = Thread.new do
52
+ loop do
53
+ StatusWorkflow.redis.expire lock_key, LOCK_EXPIRY
54
+ sleep LOCK_EXPIRY/2
55
+ end
56
+ end
57
+ blk.call
58
+ rescue
59
+ # If the block errors, set status to error and record the backtrace
60
+ error = (["#{$!.class} #{$!.message}"] + $!.backtrace).join("\n")
61
+ update_columns status: 'error', status_changed_at: Time.now, error: error
62
+ raise
63
+ end
64
+ end
65
+ # Success!
39
66
  update_columns status: to_status, status_changed_at: Time.now
67
+ ensure
68
+ StatusWorkflow.redis.del lock_key
69
+ heartbeat.kill if heartbeat
40
70
  end
41
- StatusWorkflow.redis.del lock_key
42
71
  true
43
72
  end
44
73
  define_method "can_enter_#{to_status}?" do
@@ -1,3 +1,3 @@
1
1
  module StatusWorkflow
2
- VERSION = '1.0.0'
2
+ VERSION = '1.0.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: status_workflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seamus Abshere
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-23 00:00:00.000000000 Z
11
+ date: 2018-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis