lepus 0.0.1.rc2 → 0.1.1

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +17 -4
  5. data/README.md +179 -0
  6. data/config.ru +14 -0
  7. data/docs/README.md +80 -0
  8. data/docs/cli.md +108 -0
  9. data/docs/configuration.md +171 -0
  10. data/docs/consumers.md +168 -0
  11. data/docs/getting-started.md +136 -0
  12. data/docs/images/lepus-web.png +0 -0
  13. data/docs/middleware.md +240 -0
  14. data/docs/producers.md +173 -0
  15. data/docs/prometheus.md +112 -0
  16. data/docs/rails.md +161 -0
  17. data/docs/supervisor.md +112 -0
  18. data/docs/testing.md +141 -0
  19. data/docs/web.md +85 -0
  20. data/examples/grafana-dashboard.json +450 -0
  21. data/gemfiles/Gemfile.rails-5.2 +1 -0
  22. data/gemfiles/Gemfile.rails-5.2.lock +59 -46
  23. data/gemfiles/Gemfile.rails-6.1 +1 -0
  24. data/gemfiles/Gemfile.rails-6.1.lock +72 -58
  25. data/gemfiles/Gemfile.rails-7.2.lock +8 -1
  26. data/gemfiles/Gemfile.rails-8.0.lock +8 -1
  27. data/lepus.gemspec +5 -1
  28. data/lib/lepus/cli.rb +24 -0
  29. data/lib/lepus/configuration.rb +42 -0
  30. data/lib/lepus/consumer.rb +21 -1
  31. data/lib/lepus/consumers/handler.rb +3 -1
  32. data/lib/lepus/consumers/stats.rb +70 -0
  33. data/lib/lepus/consumers/stats_registry.rb +29 -0
  34. data/lib/lepus/consumers/worker.rb +7 -6
  35. data/lib/lepus/process.rb +4 -4
  36. data/lib/lepus/process_registry/backend.rb +49 -0
  37. data/lib/lepus/process_registry/file_backend.rb +108 -0
  38. data/lib/lepus/process_registry/message_builder.rb +72 -0
  39. data/lib/lepus/process_registry/rabbitmq_backend.rb +153 -0
  40. data/lib/lepus/process_registry.rb +28 -67
  41. data/lib/lepus/prometheus/collector.rb +149 -0
  42. data/lib/lepus/prometheus/instrumentation.rb +168 -0
  43. data/lib/lepus/prometheus.rb +48 -0
  44. data/lib/lepus/publisher.rb +3 -1
  45. data/lib/lepus/supervisor.rb +9 -2
  46. data/lib/lepus/version.rb +1 -1
  47. data/lib/lepus/web/aggregator.rb +154 -0
  48. data/lib/lepus/web/api.rb +132 -0
  49. data/lib/lepus/web/app.rb +37 -0
  50. data/lib/lepus/web/management_api.rb +192 -0
  51. data/lib/lepus/web/respond_with.rb +28 -0
  52. data/lib/lepus/web.rb +238 -0
  53. data/lib/lepus.rb +5 -0
  54. data/test_offline.html +189 -0
  55. data/web/assets/css/styles.css +635 -0
  56. data/web/assets/js/app.js +6 -0
  57. data/web/assets/js/bootstrap.js +20 -0
  58. data/web/assets/js/controllers/connection_controller.js +44 -0
  59. data/web/assets/js/controllers/dashboard_controller.js +499 -0
  60. data/web/assets/js/controllers/queue_controller.js +17 -0
  61. data/web/assets/js/controllers/theme_controller.js +31 -0
  62. data/web/assets/js/offline-manager.js +233 -0
  63. data/web/assets/js/service-worker-manager.js +65 -0
  64. data/web/index.html +159 -0
  65. data/web/sw.js +144 -0
  66. metadata +103 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2928402610a7a4f5027f33e31ea7ea790f645ebb9029e1aeba9c735681d803c
4
- data.tar.gz: fab7a599c6e24140e523d215b54bb6aed06e1c40b4ee2263ffd8c26b6de726d8
3
+ metadata.gz: dd76ca8d60a80fef01e13aacd4ce59688778fcd00b0d98d448eb252d8ba018f0
4
+ data.tar.gz: b934187a671875e0ed1d818b230109f1a3a485fae298125e543a9e5e80936b56
5
5
  SHA512:
6
- metadata.gz: 68c80bbe807bb05bd6ac3acafe8a534b70ce965190ffa19589ac2dc49e6bfae14399706c0516bb0eeb4f850b27e73dbb52ea2e16165f82d9b00615746f99dd7d
7
- data.tar.gz: 8074660176b3f287a531ad9f8c73feb81b434da8fe7411d6e9b6a309e79474a6d0598b40d0439524b106733293b00b515ae7c74b4ffaf4c93e9ba3ad81000565
6
+ metadata.gz: 3e6704aa3daea26015095548a93608a3596adaba8b479493bfb43e945a3281171f5164524664768d2e784eb5fe585d1b3c4ea86b57b859f1b7943cc32239342c
7
+ data.tar.gz: babc99c5a9de11c5148c64d26599b4a3ce6b6a64296e34b51a31883e9f24622acc02327ea7a0e7cd461336e524f110d8bfb454300e386c0dd054bafabbaec56a
data/.gitignore CHANGED
@@ -5,7 +5,6 @@
5
5
  /_yardoc/
6
6
  /coverage/
7
7
  /doc/
8
- /examples/
9
8
  /pkg/
10
9
  /spec/reports/
11
10
  /tmp/
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.0.1.rc2)
4
+ lepus (0.1.1)
5
+ base64
5
6
  bunny
6
7
  concurrent-ruby
7
8
  multi_json
@@ -13,11 +14,13 @@ GEM
13
14
  specs:
14
15
  addressable (2.8.7)
15
16
  public_suffix (>= 2.0.2, < 7.0)
16
- amq-protocol (2.5.1)
17
+ amq-protocol (2.7.0)
17
18
  ast (2.4.2)
19
+ base64 (0.3.0)
18
20
  bigdecimal (3.1.8)
19
- bunny (2.24.0)
20
- amq-protocol (~> 2.3)
21
+ bunny (3.0.0)
22
+ amq-protocol (~> 2.7)
23
+ logger (~> 1, >= 1.7)
21
24
  sorted_set (~> 1, >= 1.0.2)
22
25
  coderay (1.1.3)
23
26
  concurrent-ruby (1.3.6)
@@ -35,17 +38,23 @@ GEM
35
38
  json (2.7.5)
36
39
  language_server-protocol (3.17.0.3)
37
40
  lint_roller (1.1.0)
41
+ logger (1.7.0)
38
42
  method_source (1.1.0)
39
43
  multi_json (1.15.0)
40
44
  parallel (1.26.3)
41
45
  parser (3.3.5.0)
42
46
  ast (~> 2.4.1)
43
47
  racc
48
+ prometheus_exporter (2.1.0)
49
+ webrick
44
50
  pry (0.14.2)
45
51
  coderay (~> 1.1)
46
52
  method_source (~> 1.0)
47
53
  public_suffix (5.1.1)
48
54
  racc (1.8.1)
55
+ rack (3.2.1)
56
+ rack-test (2.2.0)
57
+ rack (>= 1.3)
49
58
  rainbow (3.1.1)
50
59
  rbtree (0.4.6)
51
60
  redis (5.4.1)
@@ -112,6 +121,7 @@ GEM
112
121
  addressable (>= 2.8.0)
113
122
  crack (>= 0.3.2)
114
123
  hashdiff (>= 0.4.0, < 2.0.0)
124
+ webrick (1.9.2)
115
125
  zeitwerk (2.6.18)
116
126
 
117
127
  PLATFORMS
@@ -123,7 +133,10 @@ DEPENDENCIES
123
133
  de-dupe
124
134
  dotenv
125
135
  lepus!
136
+ prometheus_exporter (= 2.1.0)
126
137
  pry
138
+ rack (>= 2.2)
139
+ rack-test
127
140
  rspec
128
141
  rubocop
129
142
  rubocop-performance
data/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # Lepus
2
2
 
3
+ ![Lepus web dashboard](https://github.com/user-attachments/assets/a640fc43-2f53-4aa7-aede-f1464f3e6e03)
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
+ ![Lepus web dashboard — overview of supervisors, workers, and recent activity](https://github.com/user-attachments/assets/a640fc43-2f53-4aa7-aede-f1464f3e6e03)
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
+ ```