|
| 1 | +# Migration Guide |
| 2 | + |
| 3 | +This guide explains how to migrate from `async-container-supervisor` to `async-service-supervisor`. The new version provides a cleaner API built on `Async::Bus` for improved reliability and maintainability. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +`async-service-supervisor` is the successor to `async-container-supervisor`, providing the same process supervision capabilities with an improved architecture. The main changes include: |
| 8 | + |
| 9 | +- **Namespace migration**: `Async::Container::Supervisor` → `Async::Service::Supervisor`. |
| 10 | +- **Simplified worker connection API**: New `prepare!` method replaces manual worker creation. |
| 11 | +- **Bus-based communication**: Uses `Async::Bus` for more reliable IPC. |
| 12 | +- **Improved environment integration**: Better integration with `Async::Service::Managed::Environment`. |
| 13 | + |
| 14 | +## Why Migrate? |
| 15 | + |
| 16 | +The new version provides several benefits: |
| 17 | + |
| 18 | +- **Better reliability**: Built on `Async::Bus` which provides more robust communication patterns. |
| 19 | +- **Simpler API**: Less boilerplate code required for worker setup. |
| 20 | +- **Better integration**: Improved integration with the `async-service` ecosystem. |
| 21 | +- **Future-proof**: Aligned with the direction of the async-service framework. |
| 22 | + |
| 23 | +## Step-by-Step Migration |
| 24 | + |
| 25 | +### 1. Update Gem Dependency |
| 26 | + |
| 27 | +Update your `Gemfile` or `gems.rb`: |
| 28 | + |
| 29 | +```ruby |
| 30 | +# Before: |
| 31 | +gem "async-container-supervisor" |
| 32 | + |
| 33 | +# After: |
| 34 | +gem "async-service-supervisor" |
| 35 | +``` |
| 36 | + |
| 37 | +Then run: |
| 38 | + |
| 39 | +```bash |
| 40 | +$ bundle install |
| 41 | +``` |
| 42 | + |
| 43 | +### 2. Update Require Statements |
| 44 | + |
| 45 | +Update all require statements in your code: |
| 46 | + |
| 47 | +```ruby |
| 48 | +# Before: |
| 49 | +require "async/container/supervisor" |
| 50 | + |
| 51 | +# After: |
| 52 | +require "async/service/supervisor" |
| 53 | +``` |
| 54 | + |
| 55 | +### 3. Update Namespace References |
| 56 | + |
| 57 | +Replace all references to `Async::Container::Supervisor` with `Async::Service::Supervisor`: |
| 58 | + |
| 59 | +```ruby |
| 60 | +# Before: |
| 61 | +include Async::Container::Supervisor::Environment |
| 62 | +include Async::Container::Supervisor::Supervised |
| 63 | + |
| 64 | +Async::Container::Supervisor::MemoryMonitor.new(...) |
| 65 | +Async::Container::Supervisor::ProcessMonitor.new(...) |
| 66 | + |
| 67 | +# After: |
| 68 | +include Async::Service::Supervisor::Environment |
| 69 | +include Async::Service::Supervisor::Supervised |
| 70 | + |
| 71 | +Async::Service::Supervisor::MemoryMonitor.new(...) |
| 72 | +Async::Service::Supervisor::ProcessMonitor.new(...) |
| 73 | +``` |
| 74 | + |
| 75 | +### 4. Update Worker Service Setup |
| 76 | + |
| 77 | +The worker connection API has been simplified. Update your worker service implementation: |
| 78 | + |
| 79 | +**Before (`async-container-supervisor`):** |
| 80 | + |
| 81 | +```ruby |
| 82 | +class MyWorkerService < Async::Service::Generic |
| 83 | + def setup(container) |
| 84 | + super |
| 85 | + |
| 86 | + container.run(name: self.class.name, count: 4, restart: true) do |instance| |
| 87 | + Async do |
| 88 | + # Connect to the supervisor if available: |
| 89 | + if @environment.implements?(Async::Container::Supervisor::Supervised) |
| 90 | + @evaluator.make_supervised_worker(instance).run |
| 91 | + end |
| 92 | + |
| 93 | + # Mark the worker as ready: |
| 94 | + instance.ready! |
| 95 | + |
| 96 | + # Your worker logic here: |
| 97 | + loop do |
| 98 | + # Do work... |
| 99 | + sleep 1 |
| 100 | + instance.ready! |
| 101 | + end |
| 102 | + end |
| 103 | + end |
| 104 | + end |
| 105 | +end |
| 106 | +``` |
| 107 | + |
| 108 | +**After (`async-service-supervisor`):** |
| 109 | + |
| 110 | +```ruby |
| 111 | +class MyWorkerService < Async::Service::Generic |
| 112 | + def setup(container) |
| 113 | + super |
| 114 | + |
| 115 | + container.run(name: self.class.name, count: 4, restart: true) do |instance| |
| 116 | + Async do |
| 117 | + # Get the environment evaluator: |
| 118 | + evaluator = self.environment.evaluator |
| 119 | + |
| 120 | + # Prepare the instance (connects to supervisor if available): |
| 121 | + evaluator.prepare!(instance) |
| 122 | + |
| 123 | + # Mark the worker as ready: |
| 124 | + instance.ready! |
| 125 | + |
| 126 | + # Your worker logic here: |
| 127 | + loop do |
| 128 | + # Do work... |
| 129 | + sleep 1 |
| 130 | + instance.ready! |
| 131 | + end |
| 132 | + end |
| 133 | + end |
| 134 | + end |
| 135 | +end |
| 136 | +``` |
| 137 | + |
| 138 | +**Key changes:** |
| 139 | +- Removed the `implements?` check - `prepare!` handles this automatically. |
| 140 | +- Use `evaluator.prepare!(instance)` instead of `@evaluator.make_supervised_worker(instance).run`. |
| 141 | +- The `prepare!` method calls `super(instance)` first (which may invoke parent module implementations), then connects to the supervisor by calling `supervisor_worker.run`. |
| 142 | + |
| 143 | +### 5. Update Service Configuration |
| 144 | + |
| 145 | +Update your service configuration blocks: |
| 146 | + |
| 147 | +```ruby |
| 148 | +# Before: |
| 149 | +service "worker" do |
| 150 | + service_class MyWorkerService |
| 151 | + include Async::Container::Supervisor::Supervised |
| 152 | +end |
| 153 | + |
| 154 | +service "supervisor" do |
| 155 | + include Async::Container::Supervisor::Environment |
| 156 | +end |
| 157 | + |
| 158 | +# After: |
| 159 | +service "worker" do |
| 160 | + service_class MyWorkerService |
| 161 | + include Async::Service::Supervisor::Supervised |
| 162 | +end |
| 163 | + |
| 164 | +service "supervisor" do |
| 165 | + include Async::Service::Supervisor::Environment |
| 166 | +end |
| 167 | +``` |
| 168 | + |
| 169 | +### 6. Update Monitor Configuration |
| 170 | + |
| 171 | +Monitor configuration remains the same, but update the class names: |
| 172 | + |
| 173 | +```ruby |
| 174 | +# Before: |
| 175 | +service "supervisor" do |
| 176 | + include Async::Container::Supervisor::Environment |
| 177 | + |
| 178 | + monitors do |
| 179 | + [ |
| 180 | + Async::Container::Supervisor::ProcessMonitor.new(interval: 60), |
| 181 | + Async::Container::Supervisor::MemoryMonitor.new( |
| 182 | + interval: 10, |
| 183 | + maximum_size_limit: 1024 * 1024 * 500 |
| 184 | + ) |
| 185 | + ] |
| 186 | + end |
| 187 | +end |
| 188 | + |
| 189 | +# After: |
| 190 | +service "supervisor" do |
| 191 | + include Async::Service::Supervisor::Environment |
| 192 | + |
| 193 | + monitors do |
| 194 | + [ |
| 195 | + Async::Service::Supervisor::ProcessMonitor.new(interval: 60), |
| 196 | + Async::Service::Supervisor::MemoryMonitor.new( |
| 197 | + interval: 10, |
| 198 | + maximum_size_limit: 1024 * 1024 * 500 |
| 199 | + ) |
| 200 | + ] |
| 201 | + end |
| 202 | +end |
| 203 | +``` |
| 204 | + |
| 205 | +### 7. Update Bake Tasks |
| 206 | + |
| 207 | +If you're using bake tasks, update the namespace: |
| 208 | + |
| 209 | +**Before:** |
| 210 | + |
| 211 | +```bash |
| 212 | +$ bake async:container:supervisor:restart |
| 213 | +$ bake async:container:supervisor:reload |
| 214 | +$ bake async:container:supervisor:status |
| 215 | +$ bake async:container:supervisor:memory_sample duration=30 connection_id=... |
| 216 | +``` |
| 217 | + |
| 218 | +**After:** |
| 219 | + |
| 220 | +```bash |
| 221 | +$ bake async:service:supervisor:restart |
| 222 | +$ bake async:service:supervisor:reload |
| 223 | +$ bake async:service:supervisor:status |
| 224 | +``` |
| 225 | + |
| 226 | +**Note:** The `memory_sample` bake task has been removed in `async-service-supervisor`. This functionality was removed as it wasn't very useful and added complexity. If you were using memory sampling, you'll need to find alternative approaches for memory leak detection, such as using the `MemoryMonitor` with appropriate limits or external memory profiling tools. |
| 227 | + |
| 228 | +### 8. Update Programmatic Client Usage |
| 229 | + |
| 230 | +If you're using the supervisor client programmatically, update the API: |
| 231 | + |
| 232 | +**Before (`async-container-supervisor`):** |
| 233 | + |
| 234 | +```ruby |
| 235 | +client = Async::Container::Supervisor::Client.new(endpoint: endpoint) |
| 236 | +client.connect do |connection| |
| 237 | + # Direct call-based API: |
| 238 | + result = connection.call(do: :restart) |
| 239 | + result = connection.call(do: :status) |
| 240 | + result = connection.call(do: :forward, operation: {...}, connection_id: "...") |
| 241 | +end |
| 242 | +``` |
| 243 | + |
| 244 | +**After (`async-service-supervisor`):** |
| 245 | + |
| 246 | +```ruby |
| 247 | +client = Async::Service::Supervisor::Client.new(endpoint: endpoint) |
| 248 | +client.connect do |connection| |
| 249 | + # Bus-based API with controller: |
| 250 | + supervisor = connection[:supervisor] |
| 251 | + supervisor.restart |
| 252 | + supervisor.status |
| 253 | + |
| 254 | + # Worker operations use the worker controller: |
| 255 | + worker = connection[:worker] |
| 256 | + # Worker operations are available through the worker controller |
| 257 | +end |
| 258 | +``` |
| 259 | + |
| 260 | +**Key changes:** |
| 261 | +- Client now extends `Async::Bus::Client` instead of custom implementation. |
| 262 | +- Access supervisor operations via `connection[:supervisor]` instead of `connection.call(do: :...)`. |
| 263 | +- Worker operations are accessed through `connection[:worker]` controller. |
| 264 | +- The `forward` operation (used for `memory_sample` and other worker operations) has been removed - use the worker controller API instead. |
| 265 | + |
| 266 | +### 9. Update Environment Module Usage |
| 267 | + |
| 268 | +The `Environment` module now includes `Async::Service::Managed::Environment` and has additional methods: |
| 269 | + |
| 270 | +**New in `async-service-supervisor`:** |
| 271 | + |
| 272 | +```ruby |
| 273 | +service "supervisor" do |
| 274 | + include Async::Service::Supervisor::Environment |
| 275 | + |
| 276 | + # You can now override count if needed (defaults to 1): |
| 277 | + def count |
| 278 | + 1 # Always 1 for supervisor |
| 279 | + end |
| 280 | + |
| 281 | + # container_options now merges with Managed::Environment defaults |
| 282 | +end |
| 283 | +``` |
| 284 | + |
| 285 | +The `container_options` method now properly merges with `Managed::Environment` defaults, providing better integration with the async-service framework. |
| 286 | + |
| 287 | +## Architecture Changes |
| 288 | + |
| 289 | +### Communication Protocol |
| 290 | + |
| 291 | +The underlying communication protocol has changed from a custom protocol to `Async::Bus`: |
| 292 | + |
| 293 | +- **Before**: Custom `Connection` class with `call(do: :...)` API. |
| 294 | +- **After**: `Async::Bus` with controller-based API (`connection[:supervisor]`, `connection[:worker]`). |
| 295 | + |
| 296 | +This provides: |
| 297 | +- Better type safety through controller interfaces. |
| 298 | +- More reliable message delivery. |
| 299 | +- Better error handling and reconnection logic. |
| 300 | + |
| 301 | +### Worker Registration |
| 302 | + |
| 303 | +Worker registration has been refactored: |
| 304 | + |
| 305 | +- **Before**: Workers passed state hash and received UUID connection IDs. |
| 306 | +- **After**: Workers use `WorkerController` and receive sequential integer IDs. |
| 307 | + |
| 308 | +The new approach provides: |
| 309 | +- Simpler ID management. |
| 310 | +- Better controller-based API. |
| 311 | +- More explicit worker lifecycle management. |
0 commit comments