Skip to content

Commit 30975ad

Browse files
committed
Enhance config sync
1 parent 406657f commit 30975ad

File tree

19 files changed

+734
-336
lines changed

19 files changed

+734
-336
lines changed

pkg/contracts/contracts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ type Resource interface {
5151

5252
type Meta struct {
5353
Id types.Binary `db:"id"`
54-
PropertiesChecksum types.Binary `json:"-"`
54+
PropertiesChecksum types.Binary `hash:"-"`
5555
}
5656

5757
func (m *Meta) Checksum() types.Binary {

pkg/database/database.go

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func (db *Database) DeleteStreamed(
406406
defer runtime.HandleCrash()
407407
defer close(ch)
408408

409-
return db.DeleteStreamed(ctx, relation, ch, features...)
409+
return db.DeleteStreamed(ctx, relation, ch, WithCascading(), WithBlocking())
410410
})
411411
streams[TableName(relation)] = ch
412412
}
@@ -494,8 +494,33 @@ func (db *Database) DeleteStreamed(
494494

495495
// UpsertStreamed bulk upserts the specified entities via NamedBulkExec.
496496
// The upsert statement is created using BuildUpsertStmt with the first entity from the entities stream.
497-
// Bulk size is controlled via Options.MaxPlaceholdersPerStatement and
498-
// concurrency is controlled via Options.MaxConnectionsPerTable.
497+
// Bulk size is controlled via Options.MaxPlaceholdersPerStatement and concurrency is controlled via
498+
// Options.MaxConnectionsPerTable.
499+
//
500+
// This sync process consists of the following steps:
501+
//
502+
// - It initially copies the first item from the specified stream and checks if this entity type provides relations.
503+
// If so, it first traverses all these relations recursively and starts a separate goroutine and caches the streams
504+
// for the started goroutine/relations type.
505+
//
506+
// - After the relations have been resolved, another goroutine is started which consumes from the specified `entities`
507+
// chan and performs the following actions for each of the streamed entities:
508+
//
509+
// - If the consumed entity doesn't satisfy the contracts.Entity interface, it will just forward that entity to the
510+
// next stage.
511+
//
512+
// - When the entity does satisfy the contracts.Entity, it applies the filter func on this entity (which hopefully
513+
// should check for its checksums), and forwards the entity to the `forward` chan only if the filter function
514+
// returns true and initiates a database upsert stream. Regardless, whether the function returns true, it will
515+
// stream each of the child entity with the `relation.Stream()` method to the respective cached stream of the relation.
516+
//
517+
// However, when the first item doesn't satisfy the database.HasRelations interface, it will just use only two
518+
// stages for the streamed entities to be upserted:
519+
//
520+
// - The first stage just consumes from the source stream (the `entities` chan) and applies the filter function (if any)
521+
// on each of the entities. This won't forward entities for which the filter function didn't also return true as well.
522+
//
523+
// - The second stage just performs a database upsert queries for entities that were forwarded from the previous one.
499524
func (db *Database) UpsertStreamed(
500525
ctx context.Context, entities <-chan interface{}, features ...Feature,
501526
) error {
@@ -520,7 +545,7 @@ func (db *Database) UpsertStreamed(
520545
defer runtime.HandleCrash()
521546
defer close(ch)
522547

523-
return db.UpsertStreamed(ctx, ch)
548+
return db.UpsertStreamed(ctx, ch, WithCascading(), WithPreExecution(with.preExecution))
524549
})
525550
streams[TableName(relation)] = ch
526551
}
@@ -535,19 +560,30 @@ func (db *Database) UpsertStreamed(
535560

536561
for {
537562
select {
538-
case entity, more := <-source:
563+
case e, more := <-source:
539564
if !more {
540565
return nil
541566
}
542567

543-
select {
544-
case forward <- entity:
545-
case <-ctx.Done():
546-
return ctx.Err()
568+
entity, ok := e.(contracts.Entity)
569+
shouldUpsert := true
570+
if ok && with.preExecution != nil {
571+
shouldUpsert, err = with.preExecution(entity)
572+
if err != nil {
573+
return err
574+
}
575+
}
576+
577+
if shouldUpsert {
578+
select {
579+
case forward <- e:
580+
case <-ctx.Done():
581+
return ctx.Err()
582+
}
547583
}
548584

549585
select {
550-
case dup <- entity:
586+
case dup <- e:
551587
case <-ctx.Done():
552588
return ctx.Err()
553589
}
@@ -591,8 +627,50 @@ func (db *Database) UpsertStreamed(
591627
return g.Wait()
592628
}
593629

594-
return db.NamedBulkExec(
595-
ctx, stmt, db.BatchSizeByPlaceholders(placeholders), sem, forward, com.NeverSplit[any], features...)
630+
upsertEntities := make(chan interface{})
631+
g, ctx := errgroup.WithContext(ctx)
632+
g.Go(func() error {
633+
defer runtime.HandleCrash()
634+
defer close(upsertEntities)
635+
636+
for {
637+
select {
638+
case <-ctx.Done():
639+
return ctx.Err()
640+
case e, ok := <-forward:
641+
if !ok {
642+
return nil
643+
}
644+
645+
entity, ok := e.(contracts.Entity)
646+
shouldUpsert := true
647+
if ok && with.preExecution != nil {
648+
shouldUpsert, err = with.preExecution(entity)
649+
if err != nil {
650+
return err
651+
}
652+
}
653+
654+
if shouldUpsert {
655+
select {
656+
case upsertEntities <- entity:
657+
case <-ctx.Done():
658+
return ctx.Err()
659+
}
660+
}
661+
}
662+
}
663+
})
664+
665+
g.Go(func() error {
666+
defer runtime.HandleCrash()
667+
668+
return db.NamedBulkExec(
669+
ctx, stmt, db.BatchSizeByPlaceholders(placeholders), sem, upsertEntities, com.NeverSplit[any], features...,
670+
)
671+
})
672+
673+
return g.Wait()
596674
}
597675

598676
// YieldAll executes the query with the supplied scope,

pkg/database/features.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ package database
22

33
import (
44
"github.com/icinga/icinga-kubernetes/pkg/com"
5+
"github.com/icinga/icinga-kubernetes/pkg/contracts"
56
)
67

78
type Feature func(*Features)
89

10+
type PreExecFunc func(contracts.Entity) (bool, error)
11+
912
type Features struct {
10-
blocking bool
11-
cascading bool
12-
onSuccess com.ProcessBulk[any]
13+
blocking bool
14+
cascading bool
15+
onSuccess com.ProcessBulk[any]
16+
preExecution PreExecFunc
1317
}
1418

1519
func NewFeatures(features ...Feature) *Features {
@@ -33,6 +37,12 @@ func WithCascading() Feature {
3337
}
3438
}
3539

40+
func WithPreExecution(preExec PreExecFunc) Feature {
41+
return func(f *Features) {
42+
f.preExecution = preExec
43+
}
44+
}
45+
3646
func WithOnSuccess(fn com.ProcessBulk[any]) Feature {
3747
return func(f *Features) {
3848
f.onSuccess = fn

pkg/schema/v1/container.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ type Container struct {
2020
Ready types.Bool
2121
Started types.Bool
2222
RestartCount int32
23-
Devices []*ContainerDevice `db:"-"`
24-
Mounts []*ContainerMount `db:"-"`
23+
Logs string
24+
Devices []*ContainerDevice `db:"-" hash:"-"`
25+
Mounts []*ContainerMount `db:"-" hash:"-"`
2526
}
2627

2728
func (c *Container) Relations() []database.Relation {

pkg/schema/v1/daemon_set.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ type DaemonSet struct {
2020
UpdateNumberScheduled int32
2121
NumberAvailable int32
2222
NumberUnavailable int32
23-
Conditions []*DaemonSetCondition `json:"-" db:"-"`
24-
Labels []*Label `json:"-" db:"-"`
23+
Conditions []*DaemonSetCondition `db:"-" hash:"-"`
24+
Labels []*Label `db:"-" hash:"-"`
2525
}
2626

2727
type DaemonSetMeta struct {
@@ -66,7 +66,7 @@ func (d *DaemonSet) Obtain(k8s kmetav1.Object) {
6666
d.NumberAvailable = daemonSet.Status.NumberAvailable
6767
d.NumberUnavailable = daemonSet.Status.NumberUnavailable
6868

69-
d.PropertiesChecksum = types.Checksum(MustMarshalJSON(d))
69+
d.PropertiesChecksum = types.HashStruct(d)
7070

7171
for _, condition := range daemonSet.Status.Conditions {
7272
daemonCond := &DaemonSetCondition{
@@ -80,15 +80,15 @@ func (d *DaemonSet) Obtain(k8s kmetav1.Object) {
8080
Reason: condition.Reason,
8181
Message: condition.Message,
8282
}
83-
daemonCond.PropertiesChecksum = types.Checksum(MustMarshalJSON(daemonCond))
83+
daemonCond.PropertiesChecksum = types.HashStruct(daemonCond)
8484

8585
d.Conditions = append(d.Conditions, daemonCond)
8686
}
8787

8888
for labelName, labelValue := range daemonSet.Labels {
8989
label := NewLabel(labelName, labelValue)
9090
label.DaemonSetId = d.Id
91-
label.PropertiesChecksum = types.Checksum(MustMarshalJSON(label))
91+
label.PropertiesChecksum = types.HashStruct(label)
9292

9393
d.Labels = append(d.Labels, label)
9494
}

pkg/schema/v1/deployment.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ type Deployment struct {
2121
ReadyReplicas int32
2222
AvailableReplicas int32
2323
UnavailableReplicas int32
24-
Conditions []*DeploymentCondition `json:"-" db:"-"`
25-
Labels []*Label `json:"-" db:"-"`
24+
Conditions []*DeploymentCondition `db:"-" hash:"-"`
25+
Labels []*Label `db:"-" hash:"-"`
2626
}
2727

2828
type DeploymentConditionMeta struct {
@@ -79,7 +79,7 @@ func (d *Deployment) Obtain(k8s kmetav1.Object) {
7979
d.ReadyReplicas = deployment.Status.ReadyReplicas
8080
d.UnavailableReplicas = deployment.Status.UnavailableReplicas
8181

82-
d.PropertiesChecksum = types.Checksum(MustMarshalJSON(d))
82+
d.PropertiesChecksum = types.HashStruct(d)
8383

8484
for _, condition := range deployment.Status.Conditions {
8585
deploymentCond := &DeploymentCondition{
@@ -94,15 +94,15 @@ func (d *Deployment) Obtain(k8s kmetav1.Object) {
9494
Reason: condition.Reason,
9595
Message: condition.Message,
9696
}
97-
deploymentCond.PropertiesChecksum = types.Checksum(MustMarshalJSON(deploymentCond))
97+
deploymentCond.PropertiesChecksum = types.HashStruct(deploymentCond)
9898

9999
d.Conditions = append(d.Conditions, deploymentCond)
100100
}
101101

102102
for labelName, labelValue := range deployment.Labels {
103103
label := NewLabel(labelName, labelValue)
104104
label.DeploymentId = d.Id
105-
label.PropertiesChecksum = types.Checksum(MustMarshalJSON(label))
105+
label.PropertiesChecksum = types.HashStruct(label)
106106

107107
d.Labels = append(d.Labels, label)
108108
}

pkg/schema/v1/event.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ func NewEvent() contracts.Entity {
2929

3030
func (e *Event) Obtain(k8s kmetav1.Object) {
3131
e.ObtainMeta(k8s)
32-
defer func() {
33-
e.PropertiesChecksum = types.Checksum(MustMarshalJSON(e))
34-
}()
3532

3633
event := k8s.(*keventsv1.Event)
3734

@@ -56,6 +53,7 @@ func (e *Event) Obtain(k8s kmetav1.Object) {
5653
e.LastSeen = types.UnixMilli(event.DeprecatedLastTimestamp.Time)
5754
}
5855
e.Count = event.DeprecatedCount
56+
e.PropertiesChecksum = types.HashStruct(e)
5957
// e.FirstSeen = types.UnixMilli(event.EventTime.Time)
6058
// if event.Series != nil {
6159
// e.LastSeen = types.UnixMilli(event.Series.LastObservedTime.Time)

pkg/schema/v1/namespace.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
type Namespace struct {
1313
ResourceMeta
1414
Phase string
15-
Conditions []*NamespaceCondition `json:"-" db:"-"`
15+
Conditions []*NamespaceCondition `db:"-" hash:"-"`
1616
}
1717

1818
type NamespaceMeta struct {
@@ -49,7 +49,7 @@ func (n *Namespace) Obtain(k8s kmetav1.Object) {
4949
n.Id = types.Checksum(namespace.Name)
5050
n.Phase = strings.ToLower(string(namespace.Status.Phase))
5151

52-
n.PropertiesChecksum = types.Checksum(MustMarshalJSON(n))
52+
n.PropertiesChecksum = types.HashStruct(n)
5353

5454
for _, condition := range namespace.Status.Conditions {
5555
namespaceCond := &NamespaceCondition{
@@ -63,7 +63,7 @@ func (n *Namespace) Obtain(k8s kmetav1.Object) {
6363
Reason: condition.Reason,
6464
Message: condition.Message,
6565
}
66-
namespaceCond.PropertiesChecksum = types.Checksum(MustMarshalJSON(namespaceCond))
66+
namespaceCond.PropertiesChecksum = types.HashStruct(namespaceCond)
6767

6868
n.Conditions = append(n.Conditions, namespaceCond)
6969
}

pkg/schema/v1/node.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ type Node struct {
2222
MemoryCapacity int64
2323
MemoryAllocatable int64
2424
PodCapacity int64
25-
Conditions []*NodeCondition `json:"-" db:"-"`
26-
Volumes []*NodeVolume `json:"-" db:"-"`
25+
Conditions []*NodeCondition `db:"-" hash:"-"`
26+
Volumes []*NodeVolume `db:"-" hash:"-"`
2727
}
2828

2929
type NodeMeta struct {
@@ -87,7 +87,7 @@ func (n *Node) Obtain(k8s kmetav1.Object) {
8787
n.MemoryAllocatable = node.Status.Allocatable.Memory().MilliValue()
8888
n.PodCapacity = node.Status.Allocatable.Pods().Value()
8989

90-
n.PropertiesChecksum = types.Checksum(MustMarshalJSON(n))
90+
n.PropertiesChecksum = types.HashStruct(n)
9191

9292
for _, condition := range node.Status.Conditions {
9393
nodeCond := &NodeCondition{
@@ -102,7 +102,7 @@ func (n *Node) Obtain(k8s kmetav1.Object) {
102102
Reason: condition.Reason,
103103
Message: condition.Message,
104104
}
105-
nodeCond.PropertiesChecksum = types.Checksum(MustMarshalJSON(nodeCond))
105+
nodeCond.PropertiesChecksum = types.HashStruct(nodeCond)
106106

107107
n.Conditions = append(n.Conditions, nodeCond)
108108
}
@@ -125,7 +125,7 @@ func (n *Node) Obtain(k8s kmetav1.Object) {
125125
Valid: true,
126126
},
127127
}
128-
nodeVolume.PropertiesChecksum = types.Checksum(MustMarshalJSON(nodeVolume))
128+
nodeVolume.PropertiesChecksum = types.HashStruct(nodeVolume)
129129

130130
n.Volumes = append(n.Volumes, nodeVolume)
131131
}

pkg/schema/v1/persistent_volume.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type PersistentVolume struct {
2323
Phase string
2424
Reason string
2525
Message string
26-
Claim *PersistentVolumeClaimRef `json:"-" db:"-"`
26+
Claim *PersistentVolumeClaimRef `db:"-" hash:"-"`
2727
}
2828

2929
type PersistentVolumeMeta struct {
@@ -75,7 +75,7 @@ func (p *PersistentVolume) Obtain(k8s kmetav1.Object) {
7575
panic(err)
7676
}
7777

78-
p.PropertiesChecksum = types.Checksum(MustMarshalJSON(p))
78+
p.PropertiesChecksum = types.HashStruct(p)
7979

8080
p.Claim = &PersistentVolumeClaimRef{
8181
PersistentVolumeMeta: PersistentVolumeMeta{
@@ -86,7 +86,7 @@ func (p *PersistentVolume) Obtain(k8s kmetav1.Object) {
8686
Name: persistentVolume.Spec.ClaimRef.Name,
8787
Uid: persistentVolume.Spec.ClaimRef.UID,
8888
}
89-
p.Claim.PropertiesChecksum = types.Checksum(MustMarshalJSON(p.Claim))
89+
p.Claim.PropertiesChecksum = types.HashStruct(p.Claim)
9090
}
9191

9292
func (p *PersistentVolume) Relations() []database.Relation {

0 commit comments

Comments
 (0)