mlevkov opened a new issue, #4062:
URL: https://github.com/apache/iggy/issues/4062

   ## What happens
   
   `SourceManager::restart` calls `source::init_source` and then 
`source::setup_source_producer`:
   
   ```rust
   // core/connectors/runtime/src/manager/source.rs
   source::init_source(&container, 
&config.plugin_config.clone().unwrap_or_default(), plugin_id, state)?;   // :232
   info!("Source connector with ID: {plugin_id} for plugin: {key} initialized 
successfully.");
   
   let (producer, encoder, transforms) =
       source::setup_source_producer(key, config, iggy_client).await?;          
                             // :241
   ```
   
   When `setup_source_producer` fails, the `?` returns with `init_source` 
already succeeded, so the plugin holds an initialized instance under 
`plugin_id` that nothing will ever close. `details.info.id = plugin_id` is not 
reached until `:261`, so the stored id still names the previous instance and a 
later stop closes that one instead. The orphan lives for the rest of the 
process.
   
   The boot path handles the identical failure correctly, and is the model for 
the fix:
   
   ```rust
   // core/connectors/runtime/src/source.rs, the setup_source_producer error arm
   Err(error) => {                                                              
                            // :245
       let message = format!("Failed to set up source producer: {error}");
       error!("Source: {name} ({key}) - {message}");
       let connector = source_connectors.get_mut(&path).expect("source 
connector was inserted above");
       let close_result = (connector.container.iggy_source_close)(plugin_id);   
                             // :251
       ...
   }
   ```
   
   ## Why it is worth fixing rather than tolerating
   
   For a plugin whose `init_source` only allocates, an orphan is wasted memory. 
For one that acquires a process-global resource in `open()`, it is a live 
fault. The HTTP source connector in #3798 is the worked example: instances 
share one listener keyed by address, so the orphan stays joined and keeps 
answering `200` into a bridge that no `poll()` drains, holds `/health` at `503` 
because a joined instance is not polling, and makes every subsequent restart 
attempt fail on the duplicate `instance_name` it never released. A transient 
broker blip at restart time turns into an instance that is unreachable until 
the whole runtime is bounced.
   
   ## Fix
   
   Mirror the boot path: close the plugin instance on the 
`setup_source_producer` failure branch before returning. Setting 
`details.info.id` before the fallible call, or carrying the id out of the error 
arm, is what keeps a later stop from closing the wrong instance.
   
   One thing that does **not** work, in case it looks tempting: evicting on a 
`poll_is_live`-style signal from the plugin side. That flag is false for the 
whole producer-setup window, so a plugin cannot distinguish "setup still in 
progress" from "setup failed and I have been orphaned".
   
   ## Provenance
   
   Raised by @hubcio in review on #3798, twice, as out of scope for that PR, 
with the fix above. Filing it so it is tracked once #3798 merges and its review 
thread stops being where this lives. Happy to take the PR if it is not already 
spoken for.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to