Skip to content

[FEATURE]: Add synchronous APIs for PluginManager init and hook dispatch #96

Description

@araujof

PluginManager's async-only APIs force synchronous hosts to bridge into async with throwaway runtimes / block_in_place. Two related sync entry points would remove those workarounds.

1. Synchronous initialize

PluginManager::initialize is async-only (cpex-core/src/manager.rs). In Praxis, the filter factory is sync (fn from_config(...) -> Result<Box<dyn HttpFilter>, _>), so to drive init we spawn a dedicated OS thread, build a throwaway current-thread tokio runtime, and block_on(initialize())block_on on the current thread panics when a runtime is already attached (e.g. under #[tokio::test]).

A blocking variant — e.g. PluginManager::initialize_blocking(), or a builder that initializes synchronously — would let sync hosts initialize without the OS-thread + throwaway-runtime workaround.

2. Synchronous hook dispatch

invoke_named / the hook-dispatch APIs are async too. Praxis's response-body filter callback is sync (Pingora's response_body_filter is not async), so the policy filter drives the CMF post-invoke hooks with tokio::task::block_in_place(|| Handle::current().block_on(...)). That requires a multi-threaded tokio runtime and permanently rejects single-threaded (work_stealing: false) deployments for the feature.

A sync hook-dispatch entry point — e.g. invoke_named_blocking() — would let sync host callbacks run hooks without block_in_place and without dictating the host's runtime flavor. For hooks that perform async I/O, cpex would own how that blocking is driven internally rather than pushing the multi-threaded-runtime requirement onto every host.

Context: both raised in a Praxis review of the in-process policy filter (the second is the sync response-body phase vs async hooks comment).

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Status
    Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions