lepus 0.0.1.rc2 → 0.1.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 +4 -4
- data/.gitignore +0 -1
- data/Gemfile +5 -0
- data/Gemfile.lock +12 -1
- data/README.md +179 -0
- data/config.ru +14 -0
- data/docs/README.md +80 -0
- data/docs/cli.md +108 -0
- data/docs/configuration.md +171 -0
- data/docs/consumers.md +168 -0
- data/docs/getting-started.md +136 -0
- data/docs/images/lepus-web.png +0 -0
- data/docs/middleware.md +240 -0
- data/docs/producers.md +173 -0
- data/docs/prometheus.md +112 -0
- data/docs/rails.md +161 -0
- data/docs/supervisor.md +112 -0
- data/docs/testing.md +141 -0
- data/docs/web.md +85 -0
- data/examples/grafana-dashboard.json +450 -0
- data/gemfiles/Gemfile.rails-5.2 +1 -0
- data/gemfiles/Gemfile.rails-5.2.lock +59 -46
- data/gemfiles/Gemfile.rails-6.1 +1 -0
- data/gemfiles/Gemfile.rails-6.1.lock +72 -58
- data/gemfiles/Gemfile.rails-7.2.lock +8 -1
- data/gemfiles/Gemfile.rails-8.0.lock +8 -1
- data/lepus.gemspec +5 -1
- data/lib/lepus/cli.rb +24 -0
- data/lib/lepus/configuration.rb +42 -0
- data/lib/lepus/consumer.rb +12 -0
- data/lib/lepus/consumers/handler.rb +3 -1
- data/lib/lepus/consumers/stats.rb +70 -0
- data/lib/lepus/consumers/stats_registry.rb +29 -0
- data/lib/lepus/consumers/worker.rb +7 -6
- data/lib/lepus/process.rb +4 -4
- data/lib/lepus/process_registry/backend.rb +49 -0
- data/lib/lepus/process_registry/file_backend.rb +108 -0
- data/lib/lepus/process_registry/message_builder.rb +72 -0
- data/lib/lepus/process_registry/rabbitmq_backend.rb +153 -0
- data/lib/lepus/process_registry.rb +28 -67
- data/lib/lepus/prometheus/collector.rb +149 -0
- data/lib/lepus/prometheus/instrumentation.rb +168 -0
- data/lib/lepus/prometheus.rb +48 -0
- data/lib/lepus/publisher.rb +3 -1
- data/lib/lepus/supervisor.rb +9 -2
- data/lib/lepus/version.rb +1 -1
- data/lib/lepus/web/aggregator.rb +154 -0
- data/lib/lepus/web/api.rb +132 -0
- data/lib/lepus/web/app.rb +37 -0
- data/lib/lepus/web/management_api.rb +192 -0
- data/lib/lepus/web/respond_with.rb +28 -0
- data/lib/lepus/web.rb +238 -0
- data/lib/lepus.rb +5 -0
- data/test_offline.html +189 -0
- data/web/assets/css/styles.css +635 -0
- data/web/assets/js/app.js +6 -0
- data/web/assets/js/bootstrap.js +20 -0
- data/web/assets/js/controllers/connection_controller.js +44 -0
- data/web/assets/js/controllers/dashboard_controller.js +499 -0
- data/web/assets/js/controllers/queue_controller.js +17 -0
- data/web/assets/js/controllers/theme_controller.js +31 -0
- data/web/assets/js/offline-manager.js +233 -0
- data/web/assets/js/service-worker-manager.js +65 -0
- data/web/index.html +159 -0
- data/web/sw.js +144 -0
- metadata +103 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79f0f6758c114b63fdc54298ffe89a0d314fde163f38603632fd249d4136c5e3
|
|
4
|
+
data.tar.gz: a644ff3638a8be640d79c625fa8ce338cc15ab838cd12c731833716e6324775e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e607ed235f468663e71dddedd6aaed1953ef7aaa4a265cbd92454354fa1a2cd60a3495b243c750d89055db2dced9eda7eb97068aa0785c0f2d6347ed55b8908f
|
|
7
|
+
data.tar.gz: c54bbaf33e0dda1cbe46a81fb2756623c49ef1c8bdeefdc5ebae9ba988c3b8c7dfc7d00da5ec14cbbede7e8ff47c3d2e241b4244ef8db0bc67b4c7240f639bf3
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
|
@@ -6,3 +6,8 @@ source "https://rubygems.org"
|
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
8
|
gem "connection_pool", "< 3"
|
|
9
|
+
|
|
10
|
+
# prometheus_exporter 2.1.1+ requires Ruby 3.0+; 2.3+ requires Ruby 3.2+.
|
|
11
|
+
# The CI matrix runs this Gemfile on Ruby 2.7 and 3.0, so pin to a version
|
|
12
|
+
# compatible across the matrix.
|
|
13
|
+
gem "prometheus_exporter", "= 2.1.0"
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
lepus (0.
|
|
4
|
+
lepus (0.1.0)
|
|
5
|
+
base64
|
|
5
6
|
bunny
|
|
6
7
|
concurrent-ruby
|
|
7
8
|
multi_json
|
|
@@ -15,6 +16,7 @@ GEM
|
|
|
15
16
|
public_suffix (>= 2.0.2, < 7.0)
|
|
16
17
|
amq-protocol (2.5.1)
|
|
17
18
|
ast (2.4.2)
|
|
19
|
+
base64 (0.3.0)
|
|
18
20
|
bigdecimal (3.1.8)
|
|
19
21
|
bunny (2.24.0)
|
|
20
22
|
amq-protocol (~> 2.3)
|
|
@@ -41,11 +43,16 @@ GEM
|
|
|
41
43
|
parser (3.3.5.0)
|
|
42
44
|
ast (~> 2.4.1)
|
|
43
45
|
racc
|
|
46
|
+
prometheus_exporter (2.1.0)
|
|
47
|
+
webrick
|
|
44
48
|
pry (0.14.2)
|
|
45
49
|
coderay (~> 1.1)
|
|
46
50
|
method_source (~> 1.0)
|
|
47
51
|
public_suffix (5.1.1)
|
|
48
52
|
racc (1.8.1)
|
|
53
|
+
rack (3.2.1)
|
|
54
|
+
rack-test (2.2.0)
|
|
55
|
+
rack (>= 1.3)
|
|
49
56
|
rainbow (3.1.1)
|
|
50
57
|
rbtree (0.4.6)
|
|
51
58
|
redis (5.4.1)
|
|
@@ -112,6 +119,7 @@ GEM
|
|
|
112
119
|
addressable (>= 2.8.0)
|
|
113
120
|
crack (>= 0.3.2)
|
|
114
121
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
122
|
+
webrick (1.9.2)
|
|
115
123
|
zeitwerk (2.6.18)
|
|
116
124
|
|
|
117
125
|
PLATFORMS
|
|
@@ -123,7 +131,10 @@ DEPENDENCIES
|
|
|
123
131
|
de-dupe
|
|
124
132
|
dotenv
|
|
125
133
|
lepus!
|
|
134
|
+
prometheus_exporter (= 2.1.0)
|
|
126
135
|
pry
|
|
136
|
+
rack (>= 2.2)
|
|
137
|
+
rack-test
|
|
127
138
|
rspec
|
|
128
139
|
rubocop
|
|
129
140
|
rubocop-performance
|
data/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# Lepus
|
|
2
2
|
|
|
3
|
+

|
|
3
4
|
|
|
4
5
|
Lepus is a lightweight but powerful Ruby library to help you to consume and produce messages to [RabbitMQ](https://www.rabbitmq.com/) using the [Bunny](https://github.com/ruby-amqp/bunny) gem. It's similar to the Sidekiq, Faktory, ActiveJob, SolidQueue, and other libraries, but using RabbitMQ as the message broker.
|
|
5
6
|
|
|
7
|
+
## Documentation
|
|
8
|
+
|
|
9
|
+
Full guides, consumer/producer recipes, middleware reference, and the web dashboard walkthrough are published at **[gems.marcosz.com.br/lepus](https://gems.marcosz.com.br/lepus/)** — part of the [marcosgz Ruby gem catalogue](https://gems.marcosz.com.br).
|
|
10
|
+
|
|
6
11
|
## Installation
|
|
7
12
|
|
|
8
13
|
Add this line to your application's Gemfile:
|
|
@@ -572,6 +577,180 @@ plugin :lepus
|
|
|
572
577
|
|
|
573
578
|
**Note**: The Puma plugin is only available if you are using Puma 6.x or higher.
|
|
574
579
|
|
|
580
|
+
## Web UI Dashboard
|
|
581
|
+
|
|
582
|
+
Lepus includes a built-in web dashboard that provides a real-time view of your message processing infrastructure. The dashboard allows you to monitor processes, queues, connections, and consumer performance.
|
|
583
|
+
|
|
584
|
+

|
|
585
|
+
|
|
586
|
+
### Starting the Web Dashboard
|
|
587
|
+
|
|
588
|
+
You can start the web dashboard using the `lepus web` command:
|
|
589
|
+
|
|
590
|
+
```bash
|
|
591
|
+
bundle exec lepus web
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
The dashboard will be available at `http://localhost:9292` by default. You can customize the host and port:
|
|
595
|
+
|
|
596
|
+
```bash
|
|
597
|
+
bundle exec lepus web --port 3000 --host 127.0.0.1
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Web Dashboard Features
|
|
601
|
+
|
|
602
|
+
The Lepus web dashboard provides:
|
|
603
|
+
|
|
604
|
+
- **Process Monitoring**: View all running supervisors and workers with their PIDs, memory usage, and heartbeat status
|
|
605
|
+
- **Queue Management**: Monitor queue statistics including message counts, consumer connections, and memory usage
|
|
606
|
+
- **Connection Tracking**: View active RabbitMQ connections and their states
|
|
607
|
+
- **Consumer Performance**: Track processed, rejected, and errored messages per consumer
|
|
608
|
+
- **Real-time Updates**: Dashboard automatically refreshes to show current system state
|
|
609
|
+
|
|
610
|
+
### Integrating with Rails
|
|
611
|
+
|
|
612
|
+
To integrate the Lepus web dashboard into your Rails application, you can mount it as a Rack application in your routes:
|
|
613
|
+
|
|
614
|
+
```ruby
|
|
615
|
+
# config/routes.rb
|
|
616
|
+
Rails.application.routes.draw do
|
|
617
|
+
# Your existing routes...
|
|
618
|
+
|
|
619
|
+
# Mount Lepus web dashboard (simple way)
|
|
620
|
+
mount Lepus::Web => "/lepus"
|
|
621
|
+
end
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
You can also use the more explicit syntax:
|
|
625
|
+
|
|
626
|
+
```ruby
|
|
627
|
+
# config/routes.rb
|
|
628
|
+
Rails.application.routes.draw do
|
|
629
|
+
# Your existing routes...
|
|
630
|
+
|
|
631
|
+
# Mount Lepus web dashboard (explicit way)
|
|
632
|
+
mount Lepus::Web::App.build => "/lepus"
|
|
633
|
+
end
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
This will make the dashboard available at `http://your-app.com/lepus` in your Rails application.
|
|
637
|
+
|
|
638
|
+
#### Process registry backend
|
|
639
|
+
|
|
640
|
+
Lepus tracks running supervisors and workers in a **process registry**. Two
|
|
641
|
+
backends are available:
|
|
642
|
+
|
|
643
|
+
- `:file` (default for a core `require "lepus"`) — stores process data in a
|
|
644
|
+
local file under `/tmp`. Fast and dependency-free, but the file is only
|
|
645
|
+
visible to processes that share the same filesystem.
|
|
646
|
+
- `:rabbitmq` — stores the same data in a dedicated RabbitMQ queue, so every
|
|
647
|
+
process connected to the same broker sees the same registry.
|
|
648
|
+
|
|
649
|
+
**Requiring `lepus/web` automatically switches the default to `:rabbitmq`.**
|
|
650
|
+
This is because the dashboard is almost always run in a separate process (and
|
|
651
|
+
often a separate container) from the workers, and the `:file` backend cannot
|
|
652
|
+
bridge that gap — you'd see an empty dashboard even with workers running. The
|
|
653
|
+
dashboard still needs the RabbitMQ Management API for queue/connection data,
|
|
654
|
+
but the registry is what lets it discover your workers.
|
|
655
|
+
|
|
656
|
+
If you really want the file backend even with the dashboard loaded, set it
|
|
657
|
+
explicitly after your `require`:
|
|
658
|
+
|
|
659
|
+
```ruby
|
|
660
|
+
# config/initializers/lepus.rb
|
|
661
|
+
Lepus.configure do |config|
|
|
662
|
+
config.process_registry_backend = :file
|
|
663
|
+
end
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
`Lepus::Web` is a plain Rack app, so authentication is applied by wrapping it
|
|
667
|
+
in standard Rack middleware or by gating the mount with a real auth helper.
|
|
668
|
+
Rails routing `constraints:` is **not** an authentication mechanism — a falsy
|
|
669
|
+
constraint returns 404 and never prompts for credentials.
|
|
670
|
+
|
|
671
|
+
HTTP Basic Auth (wrap the Rack app):
|
|
672
|
+
|
|
673
|
+
```ruby
|
|
674
|
+
# config/routes.rb
|
|
675
|
+
require "rack/auth/basic"
|
|
676
|
+
|
|
677
|
+
lepus_web = Rack::Builder.new do
|
|
678
|
+
use Rack::Auth::Basic, "Lepus Dashboard" do |username, password|
|
|
679
|
+
ActiveSupport::SecurityUtils.secure_compare(username, ENV.fetch("LEPUS_USER")) &
|
|
680
|
+
ActiveSupport::SecurityUtils.secure_compare(password, ENV.fetch("LEPUS_PASSWORD"))
|
|
681
|
+
end
|
|
682
|
+
run Lepus::Web
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
Rails.application.routes.draw do
|
|
686
|
+
mount lepus_web => "/lepus"
|
|
687
|
+
end
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
Devise (only admins can see the dashboard):
|
|
691
|
+
|
|
692
|
+
```ruby
|
|
693
|
+
# config/routes.rb
|
|
694
|
+
Rails.application.routes.draw do
|
|
695
|
+
authenticate :user, ->(u) { u.admin? } do
|
|
696
|
+
mount Lepus::Web => "/lepus"
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
## Prometheus metrics (optional)
|
|
702
|
+
|
|
703
|
+
Lepus ships an optional integration with
|
|
704
|
+
[`prometheus_exporter`](https://github.com/discourse/prometheus_exporter). It is
|
|
705
|
+
not a required dependency and is not auto-loaded — add the gem to your `Gemfile`
|
|
706
|
+
and require `lepus/prometheus` explicitly from the Lepus process you want to
|
|
707
|
+
instrument.
|
|
708
|
+
|
|
709
|
+
```ruby
|
|
710
|
+
# Gemfile
|
|
711
|
+
gem "prometheus_exporter"
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
```ruby
|
|
715
|
+
# e.g. config/initializers/lepus.rb, or at the top of your consumer boot script
|
|
716
|
+
require "lepus/prometheus"
|
|
717
|
+
|
|
718
|
+
# Optional: poll the RabbitMQ Management API for queue-level gauges
|
|
719
|
+
# from a single process (typically the supervisor).
|
|
720
|
+
Lepus::Prometheus.watch_queues(interval: 30)
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
Requiring `lepus/prometheus` installs the necessary hooks into
|
|
724
|
+
`Lepus::Consumers::Handler` (delivery counters and latency) and
|
|
725
|
+
`Lepus::Consumers::Worker` (process RSS gauge), and subscribes to
|
|
726
|
+
`publish.lepus` notifications (publish counters). Metrics are sent over TCP to
|
|
727
|
+
the `PrometheusExporter::Client.default` client.
|
|
728
|
+
|
|
729
|
+
On the exporter side, load the bundled type collector so the server knows how
|
|
730
|
+
to turn Lepus payloads into Prometheus metrics:
|
|
731
|
+
|
|
732
|
+
```bash
|
|
733
|
+
bundle exec prometheus_exporter -a lepus/prometheus/collector
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
Point Prometheus at the exporter (default port `9394`) and import
|
|
737
|
+
[`examples/grafana-dashboard.json`](examples/grafana-dashboard.json) into
|
|
738
|
+
Grafana. The dashboard covers every metric exposed by the collector.
|
|
739
|
+
|
|
740
|
+
### Exposed metrics
|
|
741
|
+
|
|
742
|
+
| Metric | Type | Labels | Source |
|
|
743
|
+
|-------------------------------------------|-----------|-----------------------------------|--------------------------------------------|
|
|
744
|
+
| `lepus_messages_processed_total` | counter | `consumer`, `queue`, `result` | `Handler#process_delivery` |
|
|
745
|
+
| `lepus_delivery_duration_seconds` | histogram | `consumer`, `queue` | `Handler#process_delivery` |
|
|
746
|
+
| `lepus_messages_published_total` | counter | `exchange`, `routing_key` | `publish.lepus` notification |
|
|
747
|
+
| `lepus_publish_duration_seconds` | histogram | `exchange`, `routing_key` | `publish.lepus` notification |
|
|
748
|
+
| `lepus_process_rss_memory_bytes` | gauge | `kind`, `name`, `pid` | `Worker#heartbeat` |
|
|
749
|
+
| `lepus_queue_messages` | gauge | `name` | `watch_queues` via management API |
|
|
750
|
+
| `lepus_queue_messages_ready` | gauge | `name` | `watch_queues` via management API |
|
|
751
|
+
| `lepus_queue_messages_unacknowledged` | gauge | `name` | `watch_queues` via management API |
|
|
752
|
+
| `lepus_queue_consumers` | gauge | `name` | `watch_queues` via management API |
|
|
753
|
+
| `lepus_queue_memory_bytes` | gauge | `name` | `watch_queues` via management API |
|
|
575
754
|
|
|
576
755
|
## Development
|
|
577
756
|
|
data/config.ru
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift File.expand_path("lib", __dir__)
|
|
4
|
+
|
|
5
|
+
require "lepus"
|
|
6
|
+
require "lepus/web"
|
|
7
|
+
|
|
8
|
+
# Start web services for real data
|
|
9
|
+
Lepus::Web.start
|
|
10
|
+
|
|
11
|
+
# Graceful shutdown
|
|
12
|
+
at_exit { Lepus::Web.stop }
|
|
13
|
+
|
|
14
|
+
run Lepus::Web::App.build
|
data/docs/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# lepus
|
|
2
|
+
|
|
3
|
+
RabbitMQ-backed producer/consumer framework for Ruby — with a supervisor, middleware chains, a CLI, and a live web dashboard.
|
|
4
|
+
|
|
5
|
+
Think Sidekiq or SolidQueue, but on top of RabbitMQ rather than Redis or a database. Lepus handles the operational concerns (process supervision, graceful shutdown, connection pooling, signal handling, per-worker pools) so your application code can stay focused on "what does this message do".
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- [Getting started](getting-started.md) — install, define your first consumer and producer, run them
|
|
10
|
+
- [Configuration](configuration.md) — the full `Lepus.configure` DSL
|
|
11
|
+
- [Consumers](consumers.md) — queue bindings, lifecycle, result codes, retries
|
|
12
|
+
- [Producers](producers.md) — exchanges, publishing, enable/disable hooks
|
|
13
|
+
- [Middleware](middleware.md) — built-in middlewares and how to write your own
|
|
14
|
+
- [CLI](cli.md) — `lepus start`, `lepus web`
|
|
15
|
+
- [Supervisor](supervisor.md) — process model, signals, graceful shutdown
|
|
16
|
+
- [Web dashboard](web.md) — the monitoring UI
|
|
17
|
+
- [Testing](testing.md) — testing consumers and producers
|
|
18
|
+
- [Rails integration](rails.md) — Railtie, executor wrapping, Puma plugin
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# Gemfile
|
|
24
|
+
gem 'lepus'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## One-minute tour
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
# config/initializers/lepus.rb
|
|
31
|
+
Lepus.configure do |config|
|
|
32
|
+
config.rabbitmq_url = ENV.fetch('RABBITMQ_URL', 'amqp://guest:guest@localhost:5672')
|
|
33
|
+
config.connection_name = 'my-service'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# app/consumers/orders_consumer.rb
|
|
37
|
+
class OrdersConsumer < Lepus::Consumer
|
|
38
|
+
configure(
|
|
39
|
+
queue: 'orders',
|
|
40
|
+
exchange: { name: 'orders', type: :topic, durable: true },
|
|
41
|
+
routing_key: ['order.*']
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
use :json, symbolize_keys: true
|
|
45
|
+
use :max_retry, retries: 5
|
|
46
|
+
|
|
47
|
+
def perform(message)
|
|
48
|
+
Order.create!(message.payload)
|
|
49
|
+
:ack
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# app/producers/orders_producer.rb
|
|
54
|
+
class OrdersProducer < Lepus::Producer
|
|
55
|
+
configure(exchange: { name: 'orders', type: :topic, durable: true })
|
|
56
|
+
use :json
|
|
57
|
+
use :correlation_id
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Run the consumer:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bundle exec lepus start OrdersConsumer
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Publish from anywhere in your app:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
OrdersProducer.publish({ order_id: 42, total: 99.99 }, routing_key: 'order.created')
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Version & dependencies
|
|
74
|
+
|
|
75
|
+
- Ruby: `>= 2.7.0`
|
|
76
|
+
- Runtime: `bunny`, `thor`, `zeitwerk`, `concurrent-ruby`, `multi_json`
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT.
|
data/docs/cli.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# CLI
|
|
2
|
+
|
|
3
|
+
The `lepus` executable is the main operational entry point. It's a Thor-based CLI with two subcommands.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bundle exec lepus <command> [args] [options]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## `lepus start`
|
|
10
|
+
|
|
11
|
+
Boot the supervisor and run the specified consumers.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bundle exec lepus start [CONSUMER_CLASSES...] [options]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
If no classes are specified **and** `config.consumers_directory` is set, every consumer class discovered in that directory is started.
|
|
18
|
+
|
|
19
|
+
### Options
|
|
20
|
+
|
|
21
|
+
| Flag | Default | Purpose |
|
|
22
|
+
|------|---------|---------|
|
|
23
|
+
| `--debug` | `false` | Set logger level to DEBUG. |
|
|
24
|
+
| `--logfile PATH` | stdout | Write logs to a file. |
|
|
25
|
+
| `--pidfile PATH` | none | Write the supervisor PID to a file. |
|
|
26
|
+
| `--require_file PATH`, `-r PATH` | none | Require this file before starting (e.g. `config/environment.rb`). |
|
|
27
|
+
|
|
28
|
+
### Examples
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# One consumer, no framework auto-load
|
|
32
|
+
bundle exec lepus start OrdersConsumer
|
|
33
|
+
|
|
34
|
+
# Multiple consumers, Rails env loaded first
|
|
35
|
+
bundle exec lepus start OrdersConsumer PaymentsConsumer \
|
|
36
|
+
--require_file config/environment.rb
|
|
37
|
+
|
|
38
|
+
# Auto-load everything under config.consumers_directory
|
|
39
|
+
bundle exec lepus start
|
|
40
|
+
|
|
41
|
+
# Debug mode + pidfile (typical for a systemd unit)
|
|
42
|
+
bundle exec lepus start OrdersConsumer \
|
|
43
|
+
--require_file config/environment.rb \
|
|
44
|
+
--pidfile /var/run/lepus.pid \
|
|
45
|
+
--logfile /var/log/lepus.log \
|
|
46
|
+
--debug
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### What happens at start
|
|
50
|
+
|
|
51
|
+
1. Load `--require_file` if given.
|
|
52
|
+
2. Resolve consumer classes (CLI args or auto-discovered).
|
|
53
|
+
3. Group consumers by their `process.name` (defaulting to `:default`).
|
|
54
|
+
4. Fork one worker subprocess per group. Parent becomes the supervisor.
|
|
55
|
+
5. Each worker opens a Bunny channel, declares queues/exchanges/bindings, and starts consuming with its configured thread pool.
|
|
56
|
+
6. Supervisor monitors children via pipes and restarts any that crash.
|
|
57
|
+
|
|
58
|
+
See [supervisor.md](supervisor.md) for the full lifecycle.
|
|
59
|
+
|
|
60
|
+
## `lepus web`
|
|
61
|
+
|
|
62
|
+
Run the web dashboard.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
bundle exec lepus web [options]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Options
|
|
69
|
+
|
|
70
|
+
| Flag | Default | Purpose |
|
|
71
|
+
|------|---------|---------|
|
|
72
|
+
| `--port PORT`, `-p PORT` | `9292` | Port to bind. |
|
|
73
|
+
| `--host HOST`, `-o HOST` | `0.0.0.0` | Host to bind. |
|
|
74
|
+
|
|
75
|
+
### Example
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bundle exec lepus web --port 9292 --host 0.0.0.0
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Visit http://localhost:9292.
|
|
82
|
+
|
|
83
|
+
The web UI reads from the process registry backend (`config.process_registry_backend`). For multi-host visibility, set the backend to `:rabbitmq`. See [web.md](web.md).
|
|
84
|
+
|
|
85
|
+
### Mounting in Rails
|
|
86
|
+
|
|
87
|
+
Instead of running the CLI, you can mount the web app directly:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
# config/routes.rb
|
|
91
|
+
require 'lepus/web'
|
|
92
|
+
mount Lepus::Web::App, at: '/lepus'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
See [rails.md](rails.md) for authorization patterns.
|
|
96
|
+
|
|
97
|
+
## Exit codes
|
|
98
|
+
|
|
99
|
+
- `0` — graceful shutdown (SIGTERM / SIGINT).
|
|
100
|
+
- Non-zero — unrecoverable error at boot (config invalid, RabbitMQ unreachable, etc.).
|
|
101
|
+
|
|
102
|
+
Workers that crash after boot are restarted by the supervisor; the supervisor itself only exits on an unrecoverable event or a requested shutdown.
|
|
103
|
+
|
|
104
|
+
## Environment variables
|
|
105
|
+
|
|
106
|
+
- `RABBITMQ_URL` — fallback for `config.rabbitmq_url` if not explicitly set.
|
|
107
|
+
|
|
108
|
+
Everything else is configured via `Lepus.configure`. See [configuration.md](configuration.md).
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Lepus is configured via a single block:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
Lepus.configure do |config|
|
|
7
|
+
# ... see below ...
|
|
8
|
+
end
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The configuration is mutable at boot and should be set once. Values are read lazily — most only matter at worker-start time.
|
|
12
|
+
|
|
13
|
+
## Connection
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
Lepus.configure do |config|
|
|
17
|
+
config.rabbitmq_url = ENV.fetch('RABBITMQ_URL', 'amqp://guest:guest@localhost:5672')
|
|
18
|
+
config.connection_name = 'my-service'
|
|
19
|
+
config.recovery_attempts = 10 # nil = infinite
|
|
20
|
+
config.recover_from_connection_close = true
|
|
21
|
+
end
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
| Key | Default | Purpose |
|
|
25
|
+
|-----|---------|---------|
|
|
26
|
+
| `rabbitmq_url` | `ENV['RABBITMQ_URL']` or `amqp://guest:guest@localhost:5672` | Connection string |
|
|
27
|
+
| `connection_name` | gem-generated | Shown in RabbitMQ management UI — set to your service name |
|
|
28
|
+
| `recovery_attempts` | `10` | Max automatic reconnects; `nil` for infinite |
|
|
29
|
+
| `recover_from_connection_close` | `true` | Auto-recover after a clean close |
|
|
30
|
+
|
|
31
|
+
## Application metadata
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
config.application_name = 'orders-service'
|
|
35
|
+
config.management_api_url = 'http://rabbitmq:15672' # only for :rabbitmq registry backend
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Shown in the web dashboard. `management_api_url` is only required if `process_registry_backend = :rabbitmq`.
|
|
39
|
+
|
|
40
|
+
## Consumer discovery
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
config.consumers_directory = 'app/consumers'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
When present, `lepus start` with no class arguments auto-loads every class under this directory and runs all configured consumers.
|
|
47
|
+
|
|
48
|
+
## Worker pools
|
|
49
|
+
|
|
50
|
+
A **worker** is a subprocess the supervisor forks. Consumers are grouped into workers by their `process.name`.
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
config.worker(:default) do |w|
|
|
54
|
+
w.pool_size = 5 # threads per worker
|
|
55
|
+
w.pool_timeout = 10.0 # seconds before yielding to another task
|
|
56
|
+
|
|
57
|
+
w.before_fork { ActiveRecord::Base.connection_handler.clear_all_connections! }
|
|
58
|
+
w.after_fork { ActiveRecord::Base.establish_connection }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
config.worker(:high_priority, pool_size: 10)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
| Key | Default | Purpose |
|
|
65
|
+
|-----|---------|---------|
|
|
66
|
+
| `pool_size` | `2` | Max threads per worker process |
|
|
67
|
+
| `pool_timeout` | `10.0` | Seconds threads wait on queue checkout |
|
|
68
|
+
| `before_fork` | no-op | Block run in parent before fork — close sockets, drop DB connections, etc. |
|
|
69
|
+
| `after_fork` | no-op | Block run in child after fork — reconnect, reseed RNG, etc. |
|
|
70
|
+
|
|
71
|
+
Assign consumers to a named worker via their own `configure(process: { name: :high_priority })` (see [consumers.md](consumers.md)).
|
|
72
|
+
|
|
73
|
+
## Producer pool
|
|
74
|
+
|
|
75
|
+
Producers share a connection pool:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
config.producer do |p|
|
|
79
|
+
p.pool_size = 5
|
|
80
|
+
p.pool_timeout = 5.0
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Global middleware
|
|
85
|
+
|
|
86
|
+
Middleware chains run around every message published or consumed, in addition to any per-producer or per-consumer `use` calls.
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
config.producer_middlewares do |chain|
|
|
90
|
+
chain.use :instrumentation
|
|
91
|
+
chain.use :correlation_id
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
config.consumer_middlewares do |chain|
|
|
95
|
+
chain.use :json, symbolize_keys: true
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
See [middleware.md](middleware.md).
|
|
100
|
+
|
|
101
|
+
## Process registry
|
|
102
|
+
|
|
103
|
+
The supervisor keeps a registry of running processes, visible to the web dashboard.
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
config.process_registry_backend = :file # or :rabbitmq
|
|
107
|
+
config.process_heartbeat_interval = 60 # seconds
|
|
108
|
+
config.process_alive_threshold = 5 * 60 # seconds
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- `:file` — metadata in local files under `/tmp/lepus/...`. Single-host.
|
|
112
|
+
- `:rabbitmq` — metadata in RabbitMQ itself; multi-host visibility in the web dashboard.
|
|
113
|
+
|
|
114
|
+
## Rails integration
|
|
115
|
+
|
|
116
|
+
When Rails is loaded, a Railtie wires:
|
|
117
|
+
|
|
118
|
+
- `config.app_executor = Rails.application.executor` automatically
|
|
119
|
+
- `config.logger = Rails.logger`
|
|
120
|
+
|
|
121
|
+
You can override either:
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
config.app_executor = nil # disable executor wrapping
|
|
125
|
+
config.logger = MyLogger.new
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
See [rails.md](rails.md).
|
|
129
|
+
|
|
130
|
+
## Threading error handler
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
config.on_thread_error = ->(exception) {
|
|
134
|
+
Rails.error.report(exception)
|
|
135
|
+
Honeybadger.notify(exception)
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Called when a worker thread raises. Does not stop the worker.
|
|
140
|
+
|
|
141
|
+
## Logger
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
config.logger = Logger.new($stdout)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Default: `Logger.new($stdout)` (or `Rails.logger` if Rails is present). The `--debug` CLI flag sets the level to DEBUG.
|
|
148
|
+
|
|
149
|
+
## Full example
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
Lepus.configure do |config|
|
|
153
|
+
config.rabbitmq_url = ENV.fetch('RABBITMQ_URL')
|
|
154
|
+
config.connection_name = 'orders-service'
|
|
155
|
+
config.application_name = 'orders-service'
|
|
156
|
+
config.consumers_directory = 'app/consumers'
|
|
157
|
+
|
|
158
|
+
config.worker(:default) do |w|
|
|
159
|
+
w.pool_size = 5
|
|
160
|
+
w.before_fork { ActiveRecord::Base.connection_handler.clear_all_connections! }
|
|
161
|
+
w.after_fork { ActiveRecord::Base.establish_connection }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
config.producer_middlewares do |chain|
|
|
165
|
+
chain.use :instrumentation
|
|
166
|
+
chain.use :correlation_id
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
config.on_thread_error = ->(exc) { Honeybadger.notify(exc) }
|
|
170
|
+
end
|
|
171
|
+
```
|