Files
OpenFlare/backend/core/fiber.go
T
ryan afaa8f79eb feat(core): add skipped fiber state for configuration gates
Fiber 新增 SKIPPED 态与 Skip/Skipped 方法:门禁为假的插件在 Apply 之前
即被排除并释放其作用域 Context,为互斥实现(cache 与 cache_memory 等)
同时挂载由内核择一激活铺路。
2026-08-29 09:53:10 +08:00

182 lines
4.3 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package core
import (
"fmt"
"reflect"
"sync"
)
// FiberState represents the lifecycle status of a plugin instance in the Cordis micro-kernel.
type FiberState string
const (
// FiberPending indicates the plugin is waiting for its required dependencies to be provided.
FiberPending FiberState = "PENDING"
// FiberLoading indicates the plugin is currently running its Apply mounting phase.
FiberLoading FiberState = "LOADING"
// FiberActive indicates the plugin is fully mounted, active, and operating without error.
FiberActive FiberState = "ACTIVE"
// FiberUnloading indicates the plugin is tearing down its scoped effects in LIFO order.
FiberUnloading FiberState = "UNLOADING"
// FiberDisposed indicates the plugin has been completely unmounted and its context disposed.
FiberDisposed FiberState = "DISPOSED"
// FiberSkipped indicates the plugin never activated because its configuration gate
// evaluated to false, so an alternative provider took over.
FiberSkipped FiberState = "SKIPPED"
)
// Fiber wraps a Plugin instance with a dedicated scoped Context and manages its
// reactive lifecycle state machine according to Cordis spatiotemporal composability principles.
type Fiber struct {
mu sync.RWMutex
plugin Plugin
state FiberState
ctx *Context
deps []reflect.Type
err error
}
// NewFiber creates a new Fiber for the specified plugin with a child scoped context.
func NewFiber(rootCtx *Context, plugin Plugin) *Fiber {
var deps []reflect.Type
if depPlugin, ok := plugin.(DependentPlugin); ok {
deps = depPlugin.Inject()
}
return &Fiber{
plugin: plugin,
state: FiberPending,
ctx: rootCtx.Fork(),
deps: deps,
}
}
// Plugin returns the underlying Plugin instance.
func (f *Fiber) Plugin() Plugin {
return f.plugin
}
// Name returns the unique identifier of the plugin.
func (f *Fiber) Name() string {
if f.plugin == nil {
return ""
}
return f.plugin.Name()
}
// State returns the current lifecycle state of this Fiber.
func (f *Fiber) State() FiberState {
f.mu.RLock()
defer f.mu.RUnlock()
return f.state
}
// Context returns the dedicated scoped Context for this Fiber.
func (f *Fiber) Context() *Context {
return f.ctx
}
// Dependencies returns the list of required service reflect.Types.
func (f *Fiber) Dependencies() []reflect.Type {
res := make([]reflect.Type, len(f.deps))
copy(res, f.deps)
return res
}
// Error returns the latest mounting or unmounting error, if any.
func (f *Fiber) Error() error {
f.mu.RLock()
defer f.mu.RUnlock()
return f.err
}
// DependenciesSatisfied checks if all declared dependencies are present in the target Context container.
func (f *Fiber) DependenciesSatisfied(ctx *Context) bool {
if len(f.deps) == 0 {
return true
}
container := ctx.Container()
for _, dep := range f.deps {
if _, err := container.resolve(dep); err != nil {
return false
}
}
return true
}
// Load executes the plugin mounting lifecycle: PENDING -> LOADING -> ACTIVE.
func (f *Fiber) Load() error {
f.mu.Lock()
if f.state != FiberPending {
f.mu.Unlock()
return nil
}
f.state = FiberLoading
f.err = nil
f.mu.Unlock()
if err := f.plugin.Apply(f.ctx); err != nil {
f.mu.Lock()
f.err = fmt.Errorf("fiber %q: apply failed: %w", f.Name(), err)
f.state = FiberPending
_ = f.ctx.Dispose()
f.mu.Unlock()
return err
}
f.mu.Lock()
f.state = FiberActive
f.mu.Unlock()
return nil
}
// Skip transitions a pending plugin to FiberSkipped and releases its scoped Context.
// Active plugins are left untouched, which makes the call safe to replay on every
// reconciliation pass, including for plugins mounted after the first gate evaluation.
func (f *Fiber) Skip() error {
f.mu.Lock()
if f.state != FiberPending {
f.mu.Unlock()
return nil
}
f.state = FiberSkipped
f.mu.Unlock()
return f.ctx.Dispose()
}
// Skipped reports whether the plugin was excluded by its configuration gate.
func (f *Fiber) Skipped() bool {
return f.State() == FiberSkipped
}
// Unload tears down the plugin: ACTIVE -> UNLOADING -> DISPOSED.
func (f *Fiber) Unload() error {
f.mu.Lock()
if f.state != FiberActive && f.state != FiberLoading {
f.mu.Unlock()
return nil
}
f.state = FiberUnloading
f.mu.Unlock()
err := f.ctx.Dispose()
f.mu.Lock()
f.state = FiberDisposed
if err != nil {
f.err = err
}
f.mu.Unlock()
return err
}