Entry Handlers
Entry handlers process registry entries by kind. When entries are added, updated, or deleted, the registry dispatches events to matching handlers.
How It Works
The registry maintains a map of kind patterns to handlers. When an entry changes:
- Registry emits event (
entry.create,entry.update,entry.delete) - Handler registry matches entry kind against registered patterns
- Matching handlers receive the entry
- Handlers process or reject the entry
Kind Patterns
Handlers subscribe using patterns:
| Pattern | Matches |
|---|---|
http.service |
Exact match only |
http.* |
http.service, http.router, http.endpoint |
function.* |
function.lua, function.lua.bc |
Entry Listener Interface
Handlers implement registry.EntryListener:
type EntryListener interface {
Add(ctx context.Context, entry Entry) error
Update(ctx context.Context, entry Entry) error
Delete(ctx context.Context, entry Entry) error
}
Returning an error from Add rejects the entry.
Listener vs Observer
| Type | Purpose | Can Reject |
|---|---|---|
| Listener | Primary handler | Yes |
| Observer | Secondary handler (logging, metrics) | No |
handlers.RegisterListener("http.*", httpManager)
handlers.RegisterObserver("function.*", metricsCollector)
Registering Handlers
Register handlers during boot:
func MyService() boot.Component {
return boot.New(boot.P{
Name: "myservice",
DependsOn: []boot.Name{core.RegistryName},
Load: func(ctx context.Context) (context.Context, error) {
handlers := bootpkg.GetHandlerRegistry(ctx)
handlers.RegisterListener("myservice.*", manager)
return ctx, nil
},
})
}
Decoding Entry Data
Use entry.DecodeEntryConfig to unmarshal entry data:
func (m *Manager) Add(ctx context.Context, ent registry.Entry) error {
cfg, err := entry.DecodeEntryConfig[ComponentConfig](ctx, m.dtt, ent)
if err != nil {
return err
}
// Process cfg...
return nil
}
The decoder:
- Unmarshals
entry.Datainto your config struct - Populates
IDandMetafrom the entry - Calls
InitDefaults()if implemented - Calls
Validate()if implemented
Config Structure
Entry configs typically include:
type ComponentConfig struct {
ID registry.ID `json:"id"`
Meta attrs.Bag `json:"meta"`
Name string `json:"name"`
Timeout int `json:"timeout,omitempty"`
}
func (c *ComponentConfig) InitDefaults() {
if c.Timeout == 0 {
c.Timeout = 30
}
}
func (c *ComponentConfig) Validate() error {
if c.Name == "" {
return fmt.Errorf("name is required")
}
return nil
}
Transaction Support
For atomic operations across multiple entries, implement TransactionListener:
type TransactionListener interface {
Begin(ctx context.Context)
Commit(ctx context.Context)
Discard(ctx context.Context)
}
The registry calls Begin before processing a batch, then Commit on success or Discard on failure.
See Also
- Registry - Entry storage
- Architecture - Boot sequence