status_workflow 1.0.0 → 1.0.1

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 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