the_lone_dyno 0.1.1 → 1.0.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
  SHA1:
3
- metadata.gz: 57ba91bea8834c6e34ddbb1c1538ba646e9d82a0
4
- data.tar.gz: afe9c78b57d5a1d032f36f28a9e450e4a6cff5f0
3
+ metadata.gz: adca286b5e4342cdb298d996c5b0c3c4d0b3ed28
4
+ data.tar.gz: fd8ca87a0ea95f950d302adea2a98c9c7e73fd7a
5
5
  SHA512:
6
- metadata.gz: 48adc6fdab84f9347f174352227ae8e3ee9504f9a88f3774a22d0a556b9d97cd4278a8ca5d0f23ccfe1d34f5a194b8cb13ae0f1a44f95c5f19e2b5b5950a1693
7
- data.tar.gz: dada3ca815f8a0643103f834a62a29e5078f5aa84b2b3a824bb8e88188683c2983034a0775bfd7daec014b0d565f84120a13653b21ae83b8166a96e3bbdd8e88
6
+ metadata.gz: 6f5765b377228c781d175eeaac583f698956fdc98cadb65aa3696b122811eb16043f0c7e181ca3e6db7f1a83435587553d1e65fdcba95fcfe274e87b9c8399ac
7
+ data.tar.gz: cdd2373747aa9e34b8325769495d8eab1fd22dc15251c9eeb58ce658b80d93b91886b9e12bf7ef5e4c8263326348e24f5148d51239b1aef6afbd5b6c3b748923
@@ -1,5 +1,12 @@
1
1
  # A Log of Changes!
2
2
 
3
+ ## [1.0.0] - 2015-11-06
4
+
5
+ - Exclusivity is no longer provided by advisory locks instead uses a naming convention
6
+ provided by ENV['DYNO']. Prior implementation had the problem that on Puma or unicorn
7
+ the lock would be aquired by the master process but not the puma workers . Most likely we want
8
+ the workers to be doing tasks.
9
+
3
10
  ## [0.1.1] - 2015-10-12
4
11
 
5
12
  - Restrict locking behavior by process type. Default is "web".
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # TheLoneDyno
4
4
 
5
- Isolate code to only run on a certain number of Heroku dynos. Uses Postgres [advisory locks](http://www.postgresql.org/docs/9.1/static/explicit-locking.html) to isolate behavior and Postgres [LISTEN/NOTIFY](http://www.postgresql.org/docs/9.1/static/sql-notify.html) to trigger custom events.
5
+ Isolate code to only run on a certain number of Heroku dynos. Then use [LISTEN/NOTIFY](http://www.postgresql.org/docs/9.1/static/sql-notify.html) to trigger custom events on those dynos.
6
6
 
7
7
  [![Build Status](https://travis-ci.org/schneems/the_lone_dyno.svg?branch=master)](https://travis-ci.org/schneems/the_lone_dyno)
8
8
 
@@ -14,6 +14,10 @@ Why is this needed? All Heroku dynos operate independently of one another. Once
14
14
 
15
15
  > Be warned, only changing behavior on 1 dyno in your app, could cause difficult to reproduce problems. "I'm getting an error, but only once fore every 20 requests". Using this library is an advanced technique and should be implemented with care. Make sure to tell the rest of your team what you're doing, and remove the code from your codebase as soon as you're done.
16
16
 
17
+ ## How
18
+
19
+ A big change happened between version 0.1 and 1.0. We are no longer using Postgres advisory locks. Instead we're depending on the `DYNO=web.1` environment variables. This makes behavior more predictable and easier to reason about.
20
+
17
21
  ## Installation
18
22
 
19
23
  Add this line to your application's Gemfile:
@@ -115,7 +119,7 @@ TheLoneDyno.exclusive(process_type: "worker") do
115
119
  end
116
120
  ```
117
121
 
118
- This will attempt to lock any DYNO environment variable containing "worker" string. You can use a string or a regex. if the `$DYNO` environment variable isn't present or if `process_type` is set to `false` then the check will be skipped and the first process to boot will aquire the lock regardless of dyno type. This behavior is purposful so you can aquire and lock on your local develoment machine.
122
+ This will attempt to lock any DYNO environment variable containing "worker" string.
119
123
 
120
124
  ## Running on more than 1 Dyno
121
125
 
@@ -129,7 +133,7 @@ end
129
133
 
130
134
  ## Using multiple locks on the same app
131
135
 
132
- Under the hood this uses PG advisory locks. If you need to customize the default advisory key, for instance if you want to have multiple processes you want to isolate to a set of dynos you can use the `key_base:` key, for example:
136
+ If you need to customize the default listen key, for instance if you want to have multiple processes you want to isolate to a set of dynos you can use the `key_base:` key, for example:
133
137
 
134
138
  ```ruby
135
139
  TheLoneDyno.exclusive(key_base: "reticulate splines") do
@@ -145,7 +149,6 @@ end
145
149
 
146
150
  If you don't want to run your exclusive process in the background you can force it to run syncronously using `background: false`. For example:
147
151
 
148
-
149
152
  ```ruby
150
153
  TheLoneDyno.exclusive(background: false) do |signal|
151
154
  while true do
@@ -175,7 +178,7 @@ Keep in mind that your lock is only held for the duration of your block, so if t
175
178
 
176
179
  ## Connection
177
180
 
178
- By default The Lone Dyno assumes you're using Active Record and already have a connection configured. If you want to use a different ORM, you'll need to provide TheLoneDyno with an object that responds to `exec` that executes arbitrary SQL. Under the hood this library uses [pg_lock](https://github.com/heroku/pg_lock#database-connection). So that's how you can configure the connection
181
+ By default The Lone Dyno assumes you're using Active Record and already have a connection configured. If you want to use a different ORM, you'll need to provide TheLoneDyno with an object that responds to `exec` that executes arbitrary SQL. Under the hood this library uses [hey_you](https://github.com/heroku/hey_you#database-connection). So that's how you can configure the connection
179
182
 
180
183
  ```
181
184
  connection = Module do
@@ -192,7 +195,7 @@ end
192
195
  You can alternatively set the `DEFAULT_CONNECTION`
193
196
 
194
197
  ```
195
- Pg::Lock = Module do
198
+ HeyYou::DEFAULT_CONNECTION = Module do
196
199
  def self.exec(sql, bind)
197
200
  DB.fetch(sql, bind)
198
201
  end
@@ -1,76 +1,51 @@
1
1
  require "the_lone_dyno/version"
2
- require "pg_lock"
2
+ require "hey_you"
3
3
 
4
4
  module TheLoneDyno
5
5
  DEFAULT_KEY = "the_lone_dyno_hi_ho_silver"
6
- WEB_PROCESS_TYPE_REGEX = /\Aweb/
6
+ DEFAULT_PROCESS_TYPE = "web"
7
7
 
8
8
  # Use to ensure only `dynos` count of dynos are exclusively running
9
9
  # the given block
10
- def self.exclusive(background: true, dynos: 1, process_type: WEB_PROCESS_TYPE_REGEX, key_base: DEFAULT_KEY, connection: ::PgLock::DEFAULT_CONNECTION_CONNECTOR.call, &block)
10
+ def self.exclusive(dynos: 1, process_type: DEFAULT_PROCESS_TYPE, background: true, sleep: 60, ttl: 0.1, connection: ::HeyYou::DEFAULT_CONNECTION_CONNECTOR.call, key_base: DEFAULT_KEY, **args, &block)
11
+ dynos = dyno_names(dynos, process_type)
11
12
 
12
- return if process_type && ENV["DYNO"] && !ENV["DYNO"].match(process_type)
13
+ return unless dynos.include?(ENV['DYNO'])
14
+
15
+ watcher = ListenWatch.new(ENV['DYNO'] + key_base, connection)
13
16
 
14
17
  if background
15
18
  Thread.new do
16
- forever_block = Proc.new { |*args| block.call(*args); while true do; sleep 180 ; end; }
17
- Lock.new(key_base, dynos).lock(connection, &forever_block)
19
+ forever_block = Proc.new { |*a| block.call(*a); while true do; sleep 180 ; end; }
20
+ forever_block.call(watcher)
18
21
  end
19
22
  else
20
- Lock.new(key_base, dynos).lock(connection, &block)
23
+ block.call(watcher)
21
24
  end
22
25
  end
23
26
 
27
+ def self.dyno_names(dynos, process_type)
28
+ 1.upto(dynos.to_i).map { |i| "#{process_type}.#{i}" }
29
+ end
30
+
24
31
  # Use to send a custom signal to any exclusive running dynos
25
- def self.signal(payload = "", dynos: 1, key_base: DEFAULT_KEY, connection: ::PgLock::DEFAULT_CONNECTION_CONNECTOR.call, &block)
26
- Lock.new(key_base, dynos).keys.each do |key|
27
- message = "NOTIFY #{key}, '#{payload}'"
28
- puts message
29
- connection.exec(message)
32
+ def self.signal(payload = "", dynos: 1, process_type: DEFAULT_PROCESS_TYPE, key_base: DEFAULT_KEY, connection: ::HeyYou::DEFAULT_CONNECTION_CONNECTOR.call, **args, &block)
33
+ dynos = dyno_names(dynos, process_type)
34
+
35
+ dynos.each do |dyno|
36
+ HeyYou.new(channel: (dyno + key_base).gsub(".".freeze, "_".freeze), connection: connection).notify(payload)
30
37
  end
31
38
  end
32
39
 
33
40
  # Used for running a block when a pg NOTIFY is sent
34
41
  class ListenWatch
35
42
  def initialize(key, raw_connection)
36
- @key = key
43
+ @key = key.gsub(".".freeze, "_".freeze)
37
44
  @raw_connection = raw_connection
38
45
  end
39
46
 
40
47
  def watch(sleep: 60, ttl: 0.01, &block)
41
- @raw_connection.exec "LISTEN #{@key}"
42
-
43
- @thread = Thread.new do
44
- while true do
45
- sleep sleep
46
-
47
- @raw_connection.wait_for_notify(ttl) do |channel, pid, payload|
48
- block.call(payload)
49
- end
50
- end
51
- end
52
- end
53
- end
54
-
55
- # Used for generating lock and listen keys. Isolates
56
- # advisory locking behavior.
57
- class Lock
58
- def initialize(key_base, dynos, &block)
59
- @key_base = key_base.to_s
60
- @dynos = Integer(dynos)
61
- @block = block
62
- end
63
-
64
- def keys
65
- @dynos.times.map {|i| "#{@key_base}_#{i}" }
66
- end
67
-
68
- def lock(connection, &block)
69
- keys.each do |key|
70
- PgLock.new(name: key, ttl: false, connection: connection).lock do
71
- block.call(ListenWatch.new(key, connection))
72
- end
73
- end
48
+ HeyYou.new(sleep: sleep, ttl: ttl, channel: @key, connection: @raw_connection).listen(&block)
74
49
  end
75
50
  end
76
51
  end
@@ -1,3 +1,3 @@
1
1
  module TheLoneDyno
2
- VERSION = "0.1.1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_dependency "pg_lock", "~> 0.1.0"
23
+ spec.add_dependency "hey_you", "~> 0.1.1"
24
24
 
25
25
  spec.add_development_dependency "pg", ">= 0.15"
26
26
  spec.add_development_dependency "activerecord", ">= 2.3"
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: the_lone_dyno
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - schneems
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-10-12 00:00:00.000000000 Z
11
+ date: 2015-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: pg_lock
14
+ name: hey_you
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.0
19
+ version: 0.1.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.1.0
26
+ version: 0.1.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement