Skip to content

Commit 2096524

Browse files
committed
introduce ExplicitDeleteVisitor
1 parent c181e00 commit 2096524

11 files changed

+487
-36
lines changed

pkg/tree/entry.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ type Entry interface {
155155
BreadthSearch(ctx context.Context, path string) ([]Entry, error)
156156
DeepCopy(tc *TreeContext, parent Entry) (Entry, error)
157157
GetLeafVariantEntries() LeafVariantEntries
158+
// returns true if the Entry contains leafvariants (presence container, field or leaflist)
159+
HoldsLeafvariants() bool
158160
}
159161

160162
type EntryVisitor interface {
@@ -163,7 +165,7 @@ type EntryVisitor interface {
163165
}
164166

165167
type LeafVariantEntry interface {
166-
MarkDelete(onlyIntended bool)
168+
MarkDelete(onlyIntended bool) *LeafEntry
167169
GetEntry() Entry
168170
String() string
169171
}
@@ -172,5 +174,7 @@ type LeafVariantEntries interface {
172174
MarkOwnerForDeletion(owner string, onlyIntended bool) *LeafEntry
173175
GetHighestPrecedence(onlyNewOrUpdated bool, includeDefaults bool) *LeafEntry
174176
GetRunning() *LeafEntry
175-
DeleteByOwner(owner string) bool
177+
DeleteByOwner(owner string) *LeafEntry
178+
AddExplicitDeleteEntry(owner string, priority int32) *LeafEntry
179+
GetByOwner(owner string) *LeafEntry
176180
}

pkg/tree/intent_path_mapping.go

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,60 @@ import (
66
sdcpb "github.com/sdcio/sdc-protos/sdcpb"
77
)
88

9-
type DeletePaths struct {
10-
data map[string]*sdcpb.PathSet
9+
type DeletePathSet struct {
10+
data map[string]*DeletePathPrio
1111
}
1212

13-
func NewDeletePaths() *DeletePaths {
14-
return &DeletePaths{
15-
data: map[string]*sdcpb.PathSet{},
13+
func NewDeletePaths() *DeletePathSet {
14+
return &DeletePathSet{
15+
data: map[string]*DeletePathPrio{},
1616
}
1717
}
1818

19-
func (dp *DeletePaths) Add(intentName string, pathset *sdcpb.PathSet) {
20-
set, exists := dp.data[intentName]
19+
func (dp *DeletePathSet) Add(intentName string, prio int32, pathset *sdcpb.PathSet) {
20+
dpp, exists := dp.data[intentName]
2121
if !exists {
22-
set = sdcpb.NewPathSet()
23-
dp.data[intentName] = set
22+
dpp = NewDeletePathPrio(intentName, prio)
23+
dp.data[intentName] = dpp
2424
}
25-
set.Join(pathset)
25+
if pathset == nil {
26+
return
27+
}
28+
dpp.paths.Join(pathset)
2629
}
2730

28-
func (dp *DeletePaths) Items() iter.Seq[*sdcpb.Path] {
29-
return func(yield func(*sdcpb.Path) bool) {
31+
func (dp *DeletePathSet) Items() iter.Seq[*DeletePathPrio] {
32+
return func(yield func(*DeletePathPrio) bool) {
3033
for _, val := range dp.data {
31-
for path := range val.Items() {
32-
if !yield(path) {
33-
return
34-
}
34+
if !yield(val) {
35+
return
3536
}
3637
}
3738
}
3839
}
40+
41+
type DeletePathPrio struct {
42+
owner string
43+
prio int32
44+
paths *sdcpb.PathSet
45+
}
46+
47+
func NewDeletePathPrio(owner string, prio int32) *DeletePathPrio {
48+
return &DeletePathPrio{
49+
prio: prio,
50+
owner: owner,
51+
paths: sdcpb.NewPathSet(),
52+
}
53+
}
54+
55+
func (dpp *DeletePathPrio) PathItems() iter.Seq[*sdcpb.Path] {
56+
return dpp.paths.Items()
57+
}
58+
59+
func (dpp *DeletePathPrio) GetPrio() int32 {
60+
return dpp.prio
61+
}
62+
63+
func (dpp *DeletePathPrio) GetOwner() string {
64+
return dpp.owner
65+
}

pkg/tree/leaf_entry.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tree
22

33
import (
44
"fmt"
5+
"strings"
56
"sync"
67

78
"github.com/sdcio/data-server/pkg/tree/types"
@@ -65,8 +66,9 @@ func (l *LeafEntry) MarkNew() {
6566
l.IsNew = true
6667
}
6768

68-
func (l *LeafEntry) RemoveDeleteFlag() {
69+
func (l *LeafEntry) RemoveDeleteFlag() *LeafEntry {
6970
l.Delete = false
71+
return l
7072
}
7173

7274
func (l *LeafEntry) GetDeleteFlag() bool {
@@ -99,11 +101,12 @@ func (l *LeafEntry) GetNewFlag() bool {
99101
return l.IsNew
100102
}
101103

102-
func (l *LeafEntry) DropDeleteFlag() {
104+
func (l *LeafEntry) DropDeleteFlag() *LeafEntry {
103105
l.mu.Lock()
104106
defer l.mu.Unlock()
105107
l.Delete = false
106108
l.DeleteOnlyIntended = false
109+
return l
107110
}
108111

109112
// MarkDelete indicate that the entry is to be deleted
@@ -118,11 +121,30 @@ func (l *LeafEntry) MarkDelete(onlyIntended bool) {
118121
l.IsNew = false
119122
}
120123

124+
// MarkExpliciteDelete indicate that the entry is to be explicitely deleted
125+
func (l *LeafEntry) MarkExpliciteDelete() {
126+
l.mu.Lock()
127+
defer l.mu.Unlock()
128+
l.Delete = true
129+
l.IsExplicitDelete = true
130+
l.IsUpdated = false
131+
l.IsNew = false
132+
}
133+
121134
// String returns a string representation of the LeafEntry
122135
func (l *LeafEntry) String() string {
123136
return fmt.Sprintf("Owner: %s, Priority: %d, Value: %s, New: %t, Delete: %t, Update: %t, DeleteIntendedOnly: %t, ExplicitDelete: %t", l.Owner(), l.Priority(), utils.TypedValueToString(l.Value()), l.GetNewFlag(), l.GetDeleteFlag(), l.GetUpdateFlag(), l.GetDeleteOnlyIntendedFlag(), l.GetExplicitDeleteFlag())
124137
}
125138

139+
// Compare used for slices.SortFunc. Sorts by path and if equal paths then by owner as the second criteria
140+
func (l *LeafEntry) Compare(other *LeafEntry) int {
141+
result := strings.Compare(l.GetPathSlice().String(), other.GetPathSlice().String())
142+
if result != 0 {
143+
return result
144+
}
145+
return strings.Compare(l.Update.Owner(), other.Update.Owner())
146+
}
147+
126148
// NewLeafEntry constructor for a new LeafEntry
127149
func NewLeafEntry(c *types.Update, flags *types.UpdateInsertFlags, parent Entry) *LeafEntry {
128150
le := &LeafEntry{
@@ -132,3 +154,13 @@ func NewLeafEntry(c *types.Update, flags *types.UpdateInsertFlags, parent Entry)
132154
flags.Apply(le)
133155
return le
134156
}
157+
158+
func (l *LeafEntry) Equal(other *LeafEntry) bool {
159+
l.mu.Lock()
160+
defer l.mu.Unlock()
161+
equal := l.Update.Equal(other.Update)
162+
if !equal {
163+
return false
164+
}
165+
return l.Delete == other.Delete && l.IsUpdated == other.IsUpdated && l.IsNew == other.IsNew && l.IsExplicitDelete == other.IsExplicitDelete && l.DeleteOnlyIntended == other.DeleteOnlyIntended
166+
}

pkg/tree/leaf_variant_slice.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package tree
22

3-
import "github.com/sdcio/data-server/pkg/tree/types"
3+
import (
4+
"fmt"
5+
"slices"
6+
"strings"
7+
8+
"github.com/sdcio/data-server/pkg/tree/types"
9+
)
410

511
type LeafVariantSlice []*LeafEntry
612

@@ -11,3 +17,47 @@ func (lvs LeafVariantSlice) ToUpdateSlice() types.UpdateSlice {
1117
}
1218
return result
1319
}
20+
21+
// Equal checks equality of the LeafVariantSlice with the other LeafVariantSlice
22+
func (lvs LeafVariantSlice) Equal(otherLvs LeafVariantSlice) (bool, error) {
23+
24+
// check for equal length
25+
if len(lvs) != len(otherLvs) {
26+
return false, fmt.Errorf("LeafVariantSlices differ in length %d vs. %d", len(lvs), len(otherLvs))
27+
}
28+
29+
// sort lvs
30+
slices.SortFunc(lvs, func(le1, le2 *LeafEntry) int {
31+
return le1.Compare(le2)
32+
})
33+
// sort otherLvs
34+
slices.SortFunc(lvs, func(le1, le2 *LeafEntry) int {
35+
return le1.Compare(le2)
36+
})
37+
38+
// compare one by one
39+
for idx, le1 := range lvs {
40+
equal := le1.Equal(otherLvs[idx])
41+
if !equal {
42+
return false, nil
43+
}
44+
}
45+
return true, nil
46+
}
47+
48+
func (lvs LeafVariantSlice) String() string {
49+
sb := strings.Builder{}
50+
first := true
51+
sep := ""
52+
for _, item := range lvs {
53+
sb.WriteString(sep)
54+
sb.WriteString(strings.Join(item.GetPathSlice(), " "))
55+
sb.WriteString(" -> ")
56+
sb.WriteString(item.String())
57+
if first {
58+
sep = "\n"
59+
first = false
60+
}
61+
}
62+
return sb.String()
63+
}

pkg/tree/leaf_variants.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/sdcio/data-server/pkg/tree/types"
99
"github.com/sdcio/data-server/pkg/utils"
10+
sdcpb "github.com/sdcio/sdc-protos/sdcpb"
1011
log "github.com/sirupsen/logrus"
1112
)
1213

@@ -27,7 +28,7 @@ func newLeafVariants(tc *TreeContext, parentEnty Entry) *LeafVariants {
2728

2829
func (lv *LeafVariants) Add(le *LeafEntry) {
2930
if leafVariant := lv.GetByOwner(le.Owner()); leafVariant != nil {
30-
if leafVariant.Equal(le.Update) {
31+
if leafVariant.Update.Equal(le.Update) {
3132
// it seems like the element was not deleted, so drop the delete flag
3233
leafVariant.DropDeleteFlag()
3334
} else {
@@ -408,3 +409,9 @@ func (lv *LeafVariants) GetDeviations(ch chan<- *types.DeviationEntry, isActiveC
408409
}
409410

410411
}
412+
413+
func (lv *LeafVariants) AddExplicitDeleteEntry(intentName string, priority int32) *LeafEntry {
414+
le := NewLeafEntry(types.NewUpdate(lv.parentEntry.Path(), &sdcpb.TypedValue{}, priority, intentName, 0), types.NewUpdateInsertFlags().SetExplicitDeleteFlag(), lv.parentEntry)
415+
lv.Add(le)
416+
return le
417+
}

pkg/tree/root_entry.go

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ import (
1111
"github.com/sdcio/data-server/pkg/tree/importer"
1212
"github.com/sdcio/data-server/pkg/tree/tree_persist"
1313
"github.com/sdcio/data-server/pkg/tree/types"
14+
"github.com/sdcio/data-server/pkg/utils"
1415
sdcpb "github.com/sdcio/sdc-protos/sdcpb"
1516
log "github.com/sirupsen/logrus"
1617
)
1718

1819
// RootEntry the root of the cache.Update tree
1920
type RootEntry struct {
2021
*sharedEntryAttributes
21-
explicitDeletes *DeletePaths
22+
explicitDeletes *DeletePathSet
2223
}
2324

2425
var (
@@ -89,11 +90,15 @@ func (r *RootEntry) ImportConfig(ctx context.Context, basePath types.PathSlice,
8990
return err
9091
}
9192

92-
r.explicitDeletes.Add(intentName, importer.GetDeletes())
93+
r.explicitDeletes.Add(intentName, intentPrio, importer.GetDeletes())
9394

9495
return e.ImportConfig(ctx, importer, intentName, intentPrio, flags)
9596
}
9697

98+
func (r *RootEntry) AddExplicitDeletes(intentName string, priority int32, pathset *sdcpb.PathSet) {
99+
r.explicitDeletes.Add(intentName, priority, pathset)
100+
}
101+
97102
func (r *RootEntry) Validate(ctx context.Context, vCfg *config.Validation) (types.ValidationResults, *types.ValidationStatOverall) {
98103
// perform validation
99104
// we use a channel and cumulate all the errors
@@ -235,18 +240,31 @@ func (r *RootEntry) DeleteSubtreePaths(deletes types.DeleteEntriesList, intentNa
235240
}
236241

237242
func (r *RootEntry) FinishInsertionPhase(ctx context.Context) error {
243+
edvs := ExplicitDeleteVisitors{}
244+
238245
// apply the explicit deletes
239-
edv := NewExplicitDeleteVisitor()
240-
for path := range r.explicitDeletes.Items() {
241-
entry, err := r.NavigateSdcpbPath(ctx, path.Elem, true)
242-
if err != nil {
243-
log.Warnf("Applying explicit delete: path %s not found, skipping", path.ToXPath(false))
244-
}
245-
err = entry.Walk(ctx, edv)
246-
if err != nil {
247-
return err
246+
for deletePathPrio := range r.explicitDeletes.Items() {
247+
edv := NewExplicitDeleteVisitor(deletePathPrio.GetOwner(), deletePathPrio.GetPrio())
248+
249+
for path := range deletePathPrio.PathItems() {
250+
// set the priority
251+
// navigate to the stated path
252+
entry, err := r.NavigateSdcpbPath(ctx, path.GetElem(), true)
253+
if err != nil {
254+
log.Warnf("Applying explicit delete: path %s not found, skipping", path.ToXPath(false))
255+
}
256+
257+
// walk the whole branch adding the explicit delete leafvariant
258+
err = entry.Walk(ctx, edv)
259+
if err != nil {
260+
return err
261+
}
262+
edvs[deletePathPrio.GetOwner()] = edv
248263
}
249264
}
265+
log.Debugf("ExplicitDeletes added: %s", utils.MapToString(edvs.Stats(), ", ", func(k string, v int) string {
266+
return fmt.Sprintf("%s=%d", k, v)
267+
}))
250268

251269
return r.sharedEntryAttributes.FinishInsertionPhase(ctx)
252270
}

pkg/tree/sharedEntryAttributes.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,18 @@ func (s *sharedEntryAttributes) Walk(ctx context.Context, v EntryVisitor) error
440440
return nil
441441
}
442442

443+
func (s *sharedEntryAttributes) HoldsLeafvariants() bool {
444+
switch x := s.schema.GetSchema().(type) {
445+
case *sdcpb.SchemaElem_Container:
446+
return x.Container.GetIsPresence()
447+
case *sdcpb.SchemaElem_Leaflist:
448+
return true
449+
case *sdcpb.SchemaElem_Field:
450+
return true
451+
}
452+
return false
453+
}
454+
443455
// GetSchemaKeys checks for the schema of the entry, and returns the defined keys
444456
func (s *sharedEntryAttributes) GetSchemaKeys() []string {
445457
if s.schema != nil {

pkg/tree/types/update_insert_flags.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package types
22

33
type LeafEntry interface {
44
MarkDelete(onlyIntended bool)
5+
MarkExpliciteDelete()
56
MarkNew()
67
}
78

@@ -70,6 +71,9 @@ func (f *UpdateInsertFlags) Apply(le LeafEntry) *UpdateInsertFlags {
7071
le.MarkNew()
7172
return f
7273
}
74+
if f.explicitDelete {
75+
le.MarkExpliciteDelete()
76+
}
7377
return f
7478
}
7579

0 commit comments

Comments
 (0)