marloss 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 629f069429a911325be9035a724ce9216268189a6b58239754db8b83faab9688
4
- data.tar.gz: a9ce90e625c14b49a7595fb147cafabae54202097d4a4d2683cc52b27cef991a
3
+ metadata.gz: 11111f0e9d75f96a957474f886c63db4686e0329c4a4457beafb0a0aae83be2c
4
+ data.tar.gz: fea9eec8c522ca462ede12c94aa7228cf56ae6d9540d0e8b4925e3249e88c71c
5
5
  SHA512:
6
- metadata.gz: 11f765e28d25d5e63f49e06b463f3203acc9fdbf726698cd76763a86b1bae6b288237bacf6cc1d557a1c66a1845d0bf05c97b05bba114ece203ab0073f6dfacc
7
- data.tar.gz: fc3c12bf10577a5d19febd069f97bb5028d14d467ab6ce18062af4c633458f483fe116855e26a50396cb2932b7e8e5754bc56a2d582f4074c9864ccd0d98fa67
6
+ metadata.gz: 69787108adc8ef5f2c8da6cb553d3e4033492c076267af966c9ea80d8b2d25e3175a809561327dc2f13eb13bfd08e02e38827258e94a9dcb40e732ade97f3421
7
+ data.tar.gz: cab2f65c3b4191a63c6bd30b461a3b82412dbac16267ee8ca1e0272fc9859c6f97c2aad276df371c2952cc222dbf83fb632799b0bbc4ec2594cb788973c1a411
data/.gitignore CHANGED
@@ -4,3 +4,4 @@
4
4
  Gemfile.lock
5
5
  .idea
6
6
  .*.swp
7
+ console
@@ -0,0 +1,9 @@
1
+ ## 0.2.0 29/11/2017
2
+
3
+ IMPROVEMENTS:
4
+
5
+ * Add the possibility to include `Marloss`. This adds some healpers that makes it really easy to use the lock in your class
6
+
7
+ BUG FIXES:
8
+
9
+ * Add the possibility of deleting the lock, it was documented but never implemented
data/README.md CHANGED
@@ -3,16 +3,66 @@
3
3
  [![Build Status](https://travis-ci.org/eredi93/marloss.svg?branch=master)](https://travis-ci.org/eredi93/marloss)
4
4
  [![Gem Version](https://badge.fury.io/rb/marloss.svg)](http://badge.fury.io/rb/marloss)
5
5
 
6
- Marloss is a general DynamoDB-based locking implementation.
6
+ Marloss is a general DynamoDB-based lock implementation.
7
+
8
+ ![rusty-lock](https://user-images.githubusercontent.com/10990391/33243215-aa602a6c-d2d9-11e7-8fc6-d4a0c2a5b30d.jpg)
7
9
 
8
10
  ### Installation
9
11
 
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem "marloss"
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ ```sh
21
+ $ bundle
22
+ ```
23
+
24
+ Or install it yourself as:
25
+
10
26
  ```sh
11
- gem install marloss
27
+ $ gem install marloss
12
28
  ```
13
29
 
14
30
  ### Usage
15
31
 
32
+ Marloss can be use as module, with some useful heplers, or plain for more specific use cases
33
+
34
+ #### Module
35
+
36
+ Include the module to your class and set the options
37
+
38
+ ```ruby
39
+ class MyClass
40
+
41
+ include Marloss
42
+
43
+ marloss_options table: "my_table", hash_key: "ID"
44
+
45
+ end
46
+ ```
47
+
48
+ now you can simply wrap the code that needs to be locked
49
+
50
+ ```ruby
51
+ with_marloss_locker("my_lock")
52
+ # execute code
53
+ end
54
+ ```
55
+
56
+ if you have a long running task and you need to make sure you don't lose the lock
57
+
58
+ ```ruby
59
+ with_refreshed_marloss_locker("my_lock")
60
+ # execute code
61
+ end
62
+ ```
63
+
64
+ #### Plain
65
+
16
66
  Firstly, we need to initialize a lock store:
17
67
 
18
68
  ```ruby
@@ -39,7 +89,7 @@ locker.with_refreshed_lock do
39
89
  end
40
90
 
41
91
  # delete the lock
42
- locker.delete_lock
92
+ locker.release_lock
43
93
  ```
44
94
 
45
95
  ### Testing
@@ -17,4 +17,60 @@ module Marloss
17
17
  @logger = logger
18
18
  end
19
19
 
20
+ def self.included(base)
21
+ base.define_singleton_method(:marloss_options) do |opts|
22
+ if opts[:table].nil?
23
+ raise(MissingParameterError, "DynamoDB Hash Key not set")
24
+ elsif opts[:hash_key].nil?
25
+ raise(MissingParameterError, "DynamoDB Table not set")
26
+ end
27
+
28
+ define_method(:marloss_options_hash) { opts }
29
+
30
+ nil
31
+ end
32
+
33
+ base.send(:include, InstanceMethods)
34
+ end
35
+
36
+ module InstanceMethods
37
+
38
+ def marloss_store
39
+ @marloss_store ||=begin
40
+ table = marloss_options_hash[:table]
41
+ hash_key = marloss_options_hash[:hash_key]
42
+ options = marloss_options_hash.reject do |k, v|
43
+ k == :table || k == :hash_key
44
+ end
45
+
46
+ Store.new(table, hash_key, options)
47
+ end
48
+ end
49
+
50
+ def marloss_locker(name)
51
+ Locker.new(marloss_store, name)
52
+ end
53
+
54
+ def with_marloss_locker(name, opts = {})
55
+ locker = marloss_locker(name)
56
+
57
+ locker.wait_until_lock_obtained(opts)
58
+
59
+ yield
60
+
61
+ locker.release_lock
62
+ end
63
+
64
+ def with_refreshed_marloss_locker(name, opts = {})
65
+ locker = marloss_locker(name)
66
+
67
+ locker.wait_until_lock_obtained(opts)
68
+
69
+ locker.with_refreshed_lock { yield }
70
+
71
+ locker.release_lock
72
+ end
73
+
74
+ end
75
+
20
76
  end
@@ -1,9 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  module Marloss
4
- class LockNotObtainedError < StandardError
4
+
5
+ class Error < StandardError
6
+ end
7
+
8
+ class LockNotObtainedError < Error
5
9
  end
6
10
 
7
- class LockNotRefreshedError < StandardError
11
+ class LockNotRefreshedError < Error
8
12
  end
13
+
14
+ class MissingParameterError < Error
15
+ end
16
+
9
17
  end
@@ -18,6 +18,10 @@ module Marloss
18
18
  store.refresh_lock(name)
19
19
  end
20
20
 
21
+ def release_lock
22
+ store.delete_lock(name)
23
+ end
24
+
21
25
  def wait_until_lock_obtained(sleep_seconds: 3)
22
26
  store.create_lock(name)
23
27
  rescue LockNotObtainedError
@@ -32,7 +32,9 @@ module Marloss
32
32
  },
33
33
  table_name: table
34
34
  )
35
+
35
36
  Marloss.logger.info("DynamoDB table created successfully")
37
+
36
38
  client.update_time_to_live(
37
39
  table_name: table,
38
40
  time_to_live_specification: {
@@ -40,11 +42,13 @@ module Marloss
40
42
  attribute_name: "Expires"
41
43
  }
42
44
  )
45
+
43
46
  Marloss.logger.info("DynamoDB table TTL configured successfully")
44
47
  end
45
48
 
46
49
  def delete_table
47
50
  client.delete_table(table_name: table)
51
+
48
52
  Marloss.logger.info("DynamoDB table deleted successfully")
49
53
  end
50
54
 
@@ -66,9 +70,12 @@ module Marloss
66
70
  },
67
71
  condition_expression: "attribute_not_exists(#{hash_key}) OR #E < :now OR #P = :process_id"
68
72
  )
73
+
69
74
  Marloss.logger.info("Lock for #{name} created successfully, will expire in #{ttl} seconds")
70
75
  rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
76
+
71
77
  Marloss.logger.error("Failed to create lock for #{name}")
78
+
72
79
  raise(LockNotObtainedError, e.message)
73
80
  end
74
81
 
@@ -88,12 +95,19 @@ module Marloss
88
95
  update_expression: "SET #E = :expires",
89
96
  condition_expression: "attribute_exists(#{hash_key}) AND (#E < :now OR #P = :process_id)"
90
97
  )
98
+
91
99
  Marloss.logger.info("Lock for #{name} refreshed successfully, will expire in #{ttl} seconds")
92
100
  rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
101
+
93
102
  Marloss.logger.error("Failed to refresh lock for #{name}")
103
+
94
104
  raise(LockNotRefreshedError, e.message)
95
105
  end
96
106
 
107
+ def delete_lock(name)
108
+ client.delete_item(key: { hash_key => name }, table_name: table)
109
+ end
110
+
97
111
  private def process_id
98
112
  hostname = `hostname`.chomp
99
113
  pid = Process.pid
@@ -2,6 +2,6 @@
2
2
  #
3
3
  module Marloss
4
4
 
5
- VERSION = "0.1.1"
5
+ VERSION = "0.2.0"
6
6
 
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marloss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacopo Scrinzi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-26 00:00:00.000000000 Z
11
+ date: 2017-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-dynamodb
@@ -74,6 +74,7 @@ extra_rdoc_files: []
74
74
  files:
75
75
  - ".gitignore"
76
76
  - ".travis.yml"
77
+ - CHANGELOG.md
77
78
  - CONTRIBUTING.md
78
79
  - Gemfile
79
80
  - LICENSE.md