marloss 0.1.1 → 0.2.0

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