lita-external 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +49 -0
- data/lib/lita/adapters/external.rb +10 -1
- data/lib/lita/external/robot.rb +28 -11
- data/lib/lita/external/version.rb +1 -1
- data/lita-external.gemspec +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a1c2d9d25c29abacfddd161ede9780063507d99f
|
4
|
+
data.tar.gz: fde3992420ea774d4d51f3d741554559818520b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e39ea973afe6caf81d1db432dbc5bbae5cbfbd45ddf3f0a2c7160bb023aca346c699cefd46bbe71f6bc304ec5cd6f25297655a6616d137865adad278b18258f
|
7
|
+
data.tar.gz: 9b9de40182114352af9239552956afa65fddce072cf7660409aac95aaee6dba6cf11ef850cd07ed44857ab63bcccaebaf05f6f821e77d967ea61db833dc3ee9d
|
data/README.md
CHANGED
@@ -24,6 +24,55 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
TODO: Write usage instructions here
|
26
26
|
|
27
|
+
## How it works
|
28
|
+
|
29
|
+
### Context
|
30
|
+
|
31
|
+
We are are very happy with Lita, and we are using it very extensively. So much that per moment it receive too much traffic and end up being limited by the CPU (parsing JSON webhooks mostly).
|
32
|
+
|
33
|
+
When this happen, HTTP and Chat requests start to be queued and Lita becomes unresponsive. So much that a simple ping can sometimes takes minutes.
|
34
|
+
|
35
|
+
### Proof of concept
|
36
|
+
|
37
|
+
As a PoC I implemented https://github.com/Shopify/lita-external. It's basically an abstract adapter that uses 2 Redis queues as communication mechanism.
|
38
|
+
|
39
|
+
Here's what it looks like conceptually:
|
40
|
+
|
41
|
+
```
|
42
|
+
+------------+
|
43
|
+
| +-------------+
|
44
|
+
+-------> Worker 2 | |
|
45
|
+
| | <--------+ |
|
46
|
+
| +------------+ | |
|
47
|
+
| | |
|
48
|
+
+----+--+ +------------+ +----+----v----+ +-------------+ +------------+
|
49
|
+
| | | +---> +----> | | Chat |
|
50
|
+
| Nginx +----> Worker 1 | | Redis | | Master <----> Service |
|
51
|
+
| | | <---+ <----+ | | |
|
52
|
+
+-------+ +------------+ +--------------+ +-------------+ +------------+
|
53
|
+
|
54
|
+
```
|
55
|
+
|
56
|
+
- All the workers accepts HTTP requests
|
57
|
+
- Technically, the master also accept HTTP, but we simply don't send anything to it.
|
58
|
+
- We can add as many workers as we want, on multiple servers if needed.
|
59
|
+
- Incomming chat messages are serialized with `Marshal` and pushed in `lita:messages:inbound`
|
60
|
+
- All the workers maintain a `BLPOP` on `lita:messages:inbound`. When they are dispatched a message they process it in a thread pool.
|
61
|
+
- When workers need to send a chat message (or change topic or whatever), they push it in `lita:messages:outbound`.
|
62
|
+
- The master maintain a `BLPOP` on `lita:messages:outbound`, and simply send them to the chat service.
|
63
|
+
|
64
|
+
### Status
|
65
|
+
|
66
|
+
Since very recently we are running `lita-external` in production, without any problems so far (again it's very recent).
|
67
|
+
|
68
|
+
### Additional benefits
|
69
|
+
|
70
|
+
Beyond giving us more CPU capacity and horizontal capacity, it also allow give us:
|
71
|
+
|
72
|
+
- Multi server capability which is not enough but a requirement for high availability of Lita. (It also require Redis failover and master election)
|
73
|
+
- Zero downtime deploys. We can now restart lita without droping HTTP requests. We still have a very small chat downtime when restarting the master but that's more acceptable.
|
74
|
+
|
75
|
+
|
27
76
|
## Development
|
28
77
|
|
29
78
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'puma/thread_pool'
|
2
|
+
|
1
3
|
module Lita
|
2
4
|
module Adapters
|
3
5
|
class External < Adapter
|
@@ -32,7 +34,14 @@ module Lita
|
|
32
34
|
end
|
33
35
|
rescue => error
|
34
36
|
Lita.logger.error("Inbound message failed: #{error.class}: #{error.message}")
|
35
|
-
Lita.config.robot.error_handler
|
37
|
+
if Lita.config.robot.error_handler
|
38
|
+
case Lita.config.robot.error_handler.arity
|
39
|
+
when 1, -1
|
40
|
+
Lita.config.robot.error_handler.call(error)
|
41
|
+
when 2, -2
|
42
|
+
Lita.config.robot.error_handler.call(error, {})
|
43
|
+
end
|
44
|
+
end
|
36
45
|
end
|
37
46
|
end
|
38
47
|
end
|
data/lib/lita/external/robot.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
module Lita
|
2
2
|
module External
|
3
|
+
class ::Lita::Robot
|
4
|
+
def run(&block)
|
5
|
+
run_app
|
6
|
+
adapter.run(&block)
|
7
|
+
rescue Interrupt
|
8
|
+
shut_down
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
3
12
|
class Robot < ::Lita::Robot
|
13
|
+
CommandFailed = Class.new(StandardError)
|
4
14
|
|
5
15
|
class Ballot
|
6
16
|
attr_accessor :veto
|
@@ -25,8 +35,9 @@ module Lita
|
|
25
35
|
|
26
36
|
trigger(:master_loaded)
|
27
37
|
|
28
|
-
|
29
|
-
|
38
|
+
super do
|
39
|
+
watch_outbound_queue
|
40
|
+
end
|
30
41
|
end
|
31
42
|
|
32
43
|
def shut_down
|
@@ -39,24 +50,30 @@ module Lita
|
|
39
50
|
Lita.logger.info("Watching outbound queue")
|
40
51
|
until @stopping
|
41
52
|
begin
|
42
|
-
if
|
43
|
-
|
53
|
+
if payload = External.blocking_redis.blpop('messages:outbound', timeout: 1)
|
54
|
+
command, args = Marshal.load(payload.last)
|
55
|
+
Lita.logger.debug("Triggering #{command}")
|
56
|
+
begin
|
57
|
+
adapter.public_send(command, *args)
|
58
|
+
rescue RuntimeError => error
|
59
|
+
raise CommandFailed, "#{command}(#{args.map(&:inspect).join(', ')}) failed because: #{error.message}"
|
60
|
+
end
|
44
61
|
end
|
45
62
|
rescue => error
|
46
63
|
Lita.logger.error("Outbound message failed: #{error.class}: #{error.message}")
|
64
|
+
Lita.logger.debug { "Outbound message failed: command=#{command} args=#{args.inspect}" }
|
47
65
|
if Lita.config.robot.error_handler
|
48
|
-
Lita.config.robot.error_handler.
|
66
|
+
case Lita.config.robot.error_handler.arity
|
67
|
+
when 1, -1
|
68
|
+
Lita.config.robot.error_handler.call(error)
|
69
|
+
when 2, -2
|
70
|
+
Lita.config.robot.error_handler.call(error, {})
|
71
|
+
end
|
49
72
|
end
|
50
73
|
end
|
51
74
|
end
|
52
75
|
end
|
53
76
|
end
|
54
|
-
|
55
|
-
def process_outbound_command(payload)
|
56
|
-
command, args = Marshal.load(payload)
|
57
|
-
Lita.logger.debug("Triggering #{command}")
|
58
|
-
adapter.public_send(command, *args)
|
59
|
-
end
|
60
77
|
end
|
61
78
|
end
|
62
79
|
end
|
data/lita-external.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency 'lita', '
|
22
|
+
spec.add_dependency 'lita', '>= 4.7'
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.10"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lita-external
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lita
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '4.
|
19
|
+
version: '4.7'
|
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: '4.
|
26
|
+
version: '4.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
111
|
version: '0'
|
112
112
|
requirements: []
|
113
113
|
rubyforge_project:
|
114
|
-
rubygems_version: 2.
|
114
|
+
rubygems_version: 2.6.14
|
115
115
|
signing_key:
|
116
116
|
specification_version: 4
|
117
117
|
summary: Meta Lita adapter that use a redis queue
|