Skip to content

Commit 4e633fc

Browse files
committed
feat: support components to start in dependency order
1 parent 1ade640 commit 4e633fc

File tree

11 files changed

+727
-24
lines changed

11 files changed

+727
-24
lines changed

pkg/console/component.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ type consoleWebServer struct {
5151
cs consolectx.Context
5252
}
5353

54+
func (c *consoleWebServer) RequiredDependencies() []runtime.ComponentType {
55+
return []runtime.ComponentType{
56+
runtime.ResourceManager, // Console needs Manager for resource operations
57+
// Note: No need to list ResourceStore explicitly as Manager already depends on it
58+
}
59+
}
60+
5461
func (c *consoleWebServer) Type() runtime.ComponentType {
5562
return runtime.Console
5663
}

pkg/console/counter/component.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ type ManagerComponent interface {
3838

3939
var _ ManagerComponent = &managerComponent{}
4040

41+
func (cm *counterManager) RequiredDependencies() []runtime.ComponentType {
42+
return []runtime.ComponentType{
43+
runtime.ResourceStore,
44+
}
45+
}
46+
4147
type managerComponent struct {
4248
manager CounterManager
4349
}

pkg/core/bootstrap/bootstrap.go

Lines changed: 187 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package bootstrap
1919

2020
import (
2121
"context"
22+
"fmt"
23+
"github.com/duke-git/lancet/v2/slice"
2224

2325
"github.com/pkg/errors"
2426

@@ -34,45 +36,206 @@ func Bootstrap(appCtx context.Context, cfg app.AdminConfig) (runtime.Runtime, er
3436
if err != nil {
3537
return nil, err
3638
}
37-
// 0. initialize event bus
38-
if err := initEventBus(builder); err != nil {
39+
40+
// Use smart bootstrapper for intelligent component initialization
41+
bootstrapper := NewSmartBootstrapper(builder)
42+
43+
// Optional: Set bootstrap mode based on configuration
44+
// bootstrapper.SetMode(StrictMode) // Uncomment for strict dependency checking
45+
46+
// Initialize all components in dependency order
47+
if err := bootstrapper.bootstrapComponents(appCtx, cfg); err != nil {
3948
return nil, err
4049
}
41-
// 1. initialize resource store
42-
if err := initResourceStore(cfg, builder); err != nil {
50+
51+
// Build and return runtime
52+
rt, err := builder.Build()
53+
if err != nil {
4354
return nil, err
4455
}
45-
// 2. initialize discovery
46-
if err := initializeResourceDiscovery(builder); err != nil {
47-
return nil, err
56+
return rt, nil
57+
}
58+
59+
// BootstrapMode defines how components are initialized
60+
type BootstrapMode int
61+
62+
const (
63+
// CompatibleMode uses dependency declarations when available, falls back to Order()
64+
// This is the default mode for smooth migration
65+
CompatibleMode BootstrapMode = iota
66+
67+
// StrictMode requires all components to declare dependencies, ignores Order()
68+
// Use this mode for new development or after all components are migrated
69+
StrictMode
70+
)
71+
72+
// SmartBootstrapper handles intelligent component initialization
73+
type SmartBootstrapper struct {
74+
builder *runtime.Builder
75+
mode BootstrapMode
76+
}
77+
78+
// NewSmartBootstrapper creates a new smart bootstrapper
79+
func NewSmartBootstrapper(builder *runtime.Builder) *SmartBootstrapper {
80+
return &SmartBootstrapper{
81+
builder: builder,
82+
mode: CompatibleMode, // Default to compatible mode
4883
}
49-
// 3. initialize engine
50-
if err := initializeResourceEngine(builder); err != nil {
51-
return nil, err
84+
}
85+
86+
// SetMode changes the bootstrap mode
87+
func (sb *SmartBootstrapper) SetMode(mode BootstrapMode) {
88+
sb.mode = mode
89+
}
90+
91+
// bootstrapComponents initializes all components in dependency order
92+
func (sb *SmartBootstrapper) bootstrapComponents(
93+
ctx context.Context,
94+
cfg app.AdminConfig,
95+
) error {
96+
logger.Info("Starting smart component bootstrap...")
97+
98+
// Gather all components to initialize
99+
components, err := sb.gatherComponents()
100+
if err != nil {
101+
return errors.Wrap(err, "failed to gather components")
52102
}
53-
// 4. initialize resource manager
54-
if err := initResourceManager(builder); err != nil {
55-
return nil, err
103+
104+
// Sort components by dependencies
105+
ordered, err := sb.sortComponents(components)
106+
if err != nil {
107+
return errors.Wrap(err, "failed to sort components by dependencies")
56108
}
57-
// 5. initialize console
58-
if err := initializeConsole(builder); err != nil {
59-
return nil, err
109+
110+
// Initialize components in order
111+
for i, comp := range ordered {
112+
logger.Infof("[%d/%d] Initializing %s...", i+1, len(ordered), comp.Type())
113+
if err := initAndActivateComponent(sb.builder, comp); err != nil {
114+
return errors.Wrapf(err, "failed to initialize component %s", comp.Type())
115+
}
116+
logger.Infof("[%d/%d] %s initialized successfully", i+1, len(ordered), comp.Type())
60117
}
61-
// 6. initialize counter manager
62-
if err := initializeCounterManager(builder); err != nil {
63-
return nil, err
118+
119+
logger.Info("All components bootstrapped successfully")
120+
return nil
121+
}
122+
123+
// gatherComponents collects all components that need to be initialized
124+
func (sb *SmartBootstrapper) gatherComponents() ([]runtime.Component, error) {
125+
components := []runtime.Component{}
126+
127+
// Core components
128+
coreComps := []struct {
129+
name string
130+
getter func() (runtime.Component, error)
131+
}{
132+
{"EventBus", runtime.ComponentRegistry().EventBus},
133+
{"ResourceStore", runtime.ComponentRegistry().ResourceStore},
134+
{"ResourceDiscovery", runtime.ComponentRegistry().ResourceDiscovery},
135+
{"ResourceEngine", runtime.ComponentRegistry().ResourceEngine},
136+
{"ResourceManager", runtime.ComponentRegistry().ResourceManager},
137+
{"Console", runtime.ComponentRegistry().Console},
64138
}
65-
// 7. initialize diagnostics
66-
if err := initializeDiagnoticsServer(builder); err != nil {
67-
logger.Errorf("got error when init diagnotics server %s", err)
139+
140+
for _, comp := range coreComps {
141+
c, err := comp.getter()
142+
if err != nil {
143+
return nil, errors.Wrapf(err, "failed to get component %s", comp.name)
144+
}
145+
components = append(components, c)
68146
}
69-
rt, err := builder.Build()
147+
148+
// Optional components
149+
optionalComps := []struct {
150+
name string
151+
typ runtime.ComponentType
152+
}{
153+
{"CounterManager", counter.ComponentType},
154+
{"DiagnosticsServer", diagnostics.DiagnosticsServer},
155+
}
156+
157+
for _, comp := range optionalComps {
158+
c, err := runtime.ComponentRegistry().Get(comp.typ)
159+
if err != nil {
160+
logger.Warnf("Optional component %s not available: %v", comp.name, err)
161+
continue
162+
}
163+
components = append(components, c)
164+
}
165+
166+
return components, nil
167+
}
168+
169+
// sortComponents sorts components by dependency order
170+
func (sb *SmartBootstrapper) sortComponents(
171+
components []runtime.Component,
172+
) ([]runtime.Component, error) {
173+
// Categorize components
174+
withDeps := []runtime.ComponentWithDependencies{}
175+
withoutDeps := []runtime.Component{}
176+
177+
for _, comp := range components {
178+
if dep, ok := comp.(runtime.ComponentWithDependencies); ok {
179+
withDeps = append(withDeps, dep)
180+
logger.Debugf("Component %s declares dependencies: %v",
181+
comp.Type(), dep.RequiredDependencies())
182+
} else {
183+
withoutDeps = append(withoutDeps, comp)
184+
logger.Debugf("Component %s uses Order() mode: %d",
185+
comp.Type(), comp.Order())
186+
}
187+
}
188+
189+
// If no components declare dependencies, fall back to Order() sorting
190+
if len(withDeps) == 0 {
191+
logger.Info("No components declare dependencies, using Order() based initialization")
192+
return sortByOrder(components), nil
193+
}
194+
195+
// In strict mode, all components must declare dependencies
196+
if sb.mode == StrictMode && len(withoutDeps) > 0 {
197+
names := []string{}
198+
for _, comp := range withoutDeps {
199+
names = append(names, string(comp.Type()))
200+
}
201+
return nil, fmt.Errorf(
202+
"strict mode enabled but the following components don't declare dependencies: %v\n"+
203+
"Please implement RequiredDependencies() for these components",
204+
names,
205+
)
206+
}
207+
208+
// Build dependency graph and perform topological sort
209+
graph := runtime.NewDependencyGraph(components)
210+
sorted, err := graph.TopologicalSort()
70211
if err != nil {
71212
return nil, err
72213
}
73-
return rt, nil
214+
215+
// Log initialization order
216+
logger.Info("Component initialization order:")
217+
for i, comp := range sorted {
218+
logger.Infof(" %d. %s", i+1, comp.Type())
219+
}
220+
221+
return sorted, nil
74222
}
75223

224+
// sortByOrder sorts components by Order() value (legacy mode)
225+
func sortByOrder(components []runtime.Component) []runtime.Component {
226+
sorted := make([]runtime.Component, len(components))
227+
copy(sorted, components)
228+
229+
slice.SortBy(sorted, func(a, b runtime.Component) bool {
230+
return a.Order() > b.Order()
231+
})
232+
233+
return sorted
234+
}
235+
236+
// Legacy functions below are kept for reference but no longer used
237+
// They can be removed in a future version after all components are migrated
238+
76239
func initEventBus(builder *runtime.Builder) error {
77240
comp, err := runtime.ComponentRegistry().EventBus()
78241
if err != nil {

pkg/core/discovery/component.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ type discoveryComponent struct {
5757
subscriptionMgr events.SubscriptionManager
5858
}
5959

60+
func (d *discoveryComponent) RequiredDependencies() []runtime.ComponentType {
61+
return []runtime.ComponentType{
62+
runtime.EventBus, // Discovery needs EventBus for event emission
63+
runtime.ResourceStore, // Discovery needs Store for resource storage
64+
}
65+
}
66+
6067
func newDiscoveryComponent() Component {
6168
return &discoveryComponent{
6269
informers: make(map[string]Informers),

pkg/core/events/component.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ type eventBus struct {
4343
subscriberDir map[model.ResourceKind]Subscribers
4444
}
4545

46+
func (b *eventBus) RequiredDependencies() []runtime.ComponentType {
47+
return nil // EventBus has no dependencies
48+
}
49+
4650
func (b *eventBus) Type() runtime.ComponentType {
4751
return runtime.EventBus
4852
}

pkg/core/manager/component.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ type resourceManagerComponent struct {
4141
rm ResourceManager
4242
}
4343

44+
func (r *resourceManagerComponent) RequiredDependencies() []runtime.ComponentType {
45+
return []runtime.ComponentType{
46+
runtime.ResourceStore, // Manager needs Store to be initialized first
47+
}
48+
}
49+
4450
func (r *resourceManagerComponent) Type() runtime.ComponentType {
4551
return runtime.ResourceManager
4652
}

pkg/core/runtime/component.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type Attribute interface {
4242
// Type returns the type of the component
4343
Type() ComponentType
4444
// Order indicates the order of the component during bootstrap, the bigger will be started first
45+
// Deprecated: Use RequiredDependencies() instead for explicit dependency management
4546
Order() int
4647
}
4748

@@ -52,6 +53,21 @@ type Component interface {
5253
Lifecycle
5354
}
5455

56+
// Dependency declares component dependencies
57+
// Components implementing this interface will be initialized in dependency order
58+
type Dependency interface {
59+
// RequiredDependencies returns the component types that must be initialized before this component
60+
// The system will ensure all required dependencies are initialized first, or fail with a clear error
61+
RequiredDependencies() []ComponentType
62+
}
63+
64+
// ComponentWithDependencies is a component that explicitly declares its dependencies
65+
// This is the recommended way to define component initialization order
66+
type ComponentWithDependencies interface {
67+
Component
68+
Dependency
69+
}
70+
5571
// GracefulComponent is a component that supports waiting until it's finished.
5672
// It's useful if there is cleanup logic that has to be executed before the process exits
5773
// (i.e. sending SIGTERM signals to subprocesses started by this component).
@@ -70,3 +86,13 @@ type ComponentManager interface {
7086
// Start starts all components.
7187
Start(<-chan struct{}) error
7288
}
89+
90+
// BaseComponent provides default implementations for optional interfaces
91+
// Components can embed this struct to get default implementations
92+
type BaseComponent struct{}
93+
94+
// RequiredDependencies returns nil, indicating no dependencies
95+
// Components can override this method to declare their dependencies
96+
func (b *BaseComponent) RequiredDependencies() []ComponentType {
97+
return nil
98+
}

0 commit comments

Comments
 (0)