Internos del Registry
El registry es un almacén de estado versionado y orientado a eventos. Mantiene historial de versiones completo, soporta transacciones, y propaga cambios a través del event bus.
Almacenamiento de Entradas
Las entradas se almacenan como un slice ordenado con un índice de hash map para lookups O(1):
type Entry struct {
ID ID // namespace:name
Kind Kind // Tipo de entrada
Meta attrs.Bag // Metadatos
Data payload.Payload // Contenido
}
Los IDs de entrada usan el paquete unique de Go para interning—IDs idénticos comparten memoria.
Cadena de Versiones
Cada versión apunta a su padre. El cálculo de ruta usa un algoritmo de grafos para encontrar la ruta más corta entre cualquier dos versiones:
flowchart LR
v0[v0] --> v1[v1] --> v2[v2] --> v3[v3] --> vN[vN]
ChangeSets
Un changeset es una lista ordenada de operaciones que transforman un estado a otro:
| Operación | OriginalEntry | Propósito |
|---|---|---|
| Create | nil | Agregar nueva entrada |
| Update | valor anterior | Modificar existente |
| Delete | valor eliminado | Remover entrada |
OriginalEntry permite reversión—updates almacenan el valor previo, deletes almacenan lo que fue removido.
Construir Deltas
BuildDelta(oldState, newState) genera operaciones mínimas:
- Comparar estados, identificar cambios
- Ordenar deletes en orden inverso de dependencias (dependientes primero)
- Ordenar creates/updates en orden de dependencias (dependencias primero)
Squashing
Múltiples changesets se fusionan rastreando estado final por entrada:
Create + Update = Create (con valor actualizado)
Create + Delete = ∅ (se cancelan)
Update + Delete = Delete
Delete + Create = Update
Transacciones
sequenceDiagram
participant R as Registry
participant B as EventBus
participant H as Handlers
R->>B: registry.begin
loop Cada Operación
R->>B: entry.create/update/delete
B->>H: dispatch a listeners
H-->>B: aceptar o rechazar
B-->>R: confirmación
end
alt Todo aceptado
R->>B: registry.commit
else Alguno rechazado
R->>B: registry.discard
R->>R: rollback
end
Los handlers tienen 30 segundos para aceptar o rechazar cada operación. En rechazo, el registry hace rollback calculando y aplicando el delta inverso.
Entradas No Propagantes
Algunos kinds omiten el event bus completamente:
registry.entry- Configs de aplicaciónns.requirement- Requisitos de namespacens.dependency- Dependencias de módulos
Resolución de Dependencias
Las entradas pueden declarar dependencias en otras entradas. El resolver extrae dependencias vía patrones registrados:
resolver.RegisterPattern(PathConfig{
Path: "meta.server",
AllowWildcard: true,
})
Las dependencias se extraen de campos Meta y Data de entradas, luego se usan para ordenamiento topológico durante transiciones de estado.
Historial de Versiones
Backends de historial:
| Implementación | Caso de Uso |
|---|---|
| SQLite | Persistencia de producción |
| Memory | Testing |
| Nil | Sin historial |
SQLite usa modo WAL con tablas para versiones, changesets (codificados MessagePack), y metadatos.
Navegación
El cálculo de ruta encuentra la ruta más corta entre versiones:
Path(v0, v3) = [v1, v2, v3] // Aplicar changesets hacia adelante
Path(v3, v1) = [v2, v1] // Aplicar changesets revertidos
LoadState() reproduce historial desde una línea base sin crear nuevas versiones—usado durante boot.
Finder
Motor de consultas con cache LRU para buscar entradas:
| Operador | Prefijo | Ejemplo |
|---|---|---|
| Glob | (ninguno) | .kind=function.* |
| Regex | ~ |
~meta.path=/api/.* |
| Contains | * |
*meta.tags=backend |
| Prefix | ^ |
^meta.name=user |
| Suffix | $ |
$meta.path=Handler |
Cache se invalida en cambio de versión.