@@ -23,68 +23,54 @@ import (
23
23
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
24
24
"oras.land/oras-go/v2/content"
25
25
"oras.land/oras-go/v2/errdef"
26
+ "oras.land/oras-go/v2/internal/container/set"
26
27
"oras.land/oras-go/v2/internal/descriptor"
27
28
"oras.land/oras-go/v2/internal/status"
28
29
"oras.land/oras-go/v2/internal/syncutil"
29
30
)
30
31
31
32
// Memory is a memory based PredecessorFinder.
32
33
type Memory struct {
33
- predecessors sync.Map // map[descriptor.Descriptor]map[descriptor.Descriptor]ocispec.Descriptor
34
- indexed sync.Map // map[descriptor.Descriptor]any
34
+ nodes map [descriptor.Descriptor ]ocispec.Descriptor // nodes saves the map keys of ocispec.Descriptor
35
+ predecessors map [descriptor.Descriptor ]set.Set [descriptor.Descriptor ]
36
+ successors map [descriptor.Descriptor ]set.Set [descriptor.Descriptor ]
37
+ lock sync.RWMutex
35
38
}
36
39
37
40
// NewMemory creates a new memory PredecessorFinder.
38
41
func NewMemory () * Memory {
39
- return & Memory {}
42
+ return & Memory {
43
+ nodes : make (map [descriptor.Descriptor ]ocispec.Descriptor ),
44
+ predecessors : make (map [descriptor.Descriptor ]set.Set [descriptor.Descriptor ]),
45
+ successors : make (map [descriptor.Descriptor ]set.Set [descriptor.Descriptor ]),
46
+ }
40
47
}
41
48
42
49
// Index indexes predecessors for each direct successor of the given node.
43
- // There is no data consistency issue as long as deletion is not implemented
44
- // for the underlying storage.
45
50
func (m * Memory ) Index (ctx context.Context , fetcher content.Fetcher , node ocispec.Descriptor ) error {
46
- successors , err := content .Successors (ctx , fetcher , node )
47
- if err != nil {
48
- return err
49
- }
50
-
51
- m .index (ctx , node , successors )
52
- return nil
51
+ _ , err := m .index (ctx , fetcher , node )
52
+ return err
53
53
}
54
54
55
55
// Index indexes predecessors for all the successors of the given node.
56
- // There is no data consistency issue as long as deletion is not implemented
57
- // for the underlying storage.
58
56
func (m * Memory ) IndexAll (ctx context.Context , fetcher content.Fetcher , node ocispec.Descriptor ) error {
59
57
// track content status
60
58
tracker := status .NewTracker ()
61
-
62
59
var fn syncutil.GoFunc [ocispec.Descriptor ]
63
60
fn = func (ctx context.Context , region * syncutil.LimitedRegion , desc ocispec.Descriptor ) error {
64
61
// skip the node if other go routine is working on it
65
62
_ , committed := tracker .TryCommit (desc )
66
63
if ! committed {
67
64
return nil
68
65
}
69
-
70
- // skip the node if it has been indexed
71
- key := descriptor .FromOCI (desc )
72
- _ , exists := m .indexed .Load (key )
73
- if exists {
74
- return nil
75
- }
76
-
77
- successors , err := content .Successors (ctx , fetcher , desc )
66
+ successors , err := m .index (ctx , fetcher , desc )
78
67
if err != nil {
79
68
if errors .Is (err , errdef .ErrNotFound ) {
80
69
// skip the node if it does not exist
81
70
return nil
82
71
}
83
72
return err
84
73
}
85
- m .index (ctx , desc , successors )
86
- m .indexed .Store (key , nil )
87
-
88
74
if len (successors ) > 0 {
89
75
// traverse and index successors
90
76
return syncutil .Go (ctx , nil , fn , successors ... )
@@ -96,39 +82,73 @@ func (m *Memory) IndexAll(ctx context.Context, fetcher content.Fetcher, node oci
96
82
97
83
// Predecessors returns the nodes directly pointing to the current node.
98
84
// Predecessors returns nil without error if the node does not exists in the
99
- // store.
100
- // Like other operations, calling Predecessors() is go-routine safe. However,
101
- // it does not necessarily correspond to any consistent snapshot of the stored
102
- // contents.
85
+ // store. Like other operations, calling Predecessors() is go-routine safe.
86
+ // However, it does not necessarily correspond to any consistent snapshot of
87
+ // the stored contents.
103
88
func (m * Memory ) Predecessors (_ context.Context , node ocispec.Descriptor ) ([]ocispec.Descriptor , error ) {
89
+ m .lock .RLock ()
90
+ defer m .lock .RUnlock ()
91
+
104
92
key := descriptor .FromOCI (node )
105
- value , exists := m .predecessors . Load ( key )
93
+ set , exists := m .predecessors [ key ]
106
94
if ! exists {
107
95
return nil , nil
108
96
}
109
- predecessors := value .(* sync.Map )
110
-
111
97
var res []ocispec.Descriptor
112
- predecessors .Range (func (key , value interface {}) bool {
113
- res = append (res , value .(ocispec.Descriptor ))
114
- return true
115
- })
98
+ for k := range set {
99
+ res = append (res , m .nodes [k ])
100
+ }
116
101
return res , nil
117
102
}
118
103
104
+ // Remove removes the node from its predecessors and successors.
105
+ func (m * Memory ) Remove (ctx context.Context , node ocispec.Descriptor ) error {
106
+ m .lock .Lock ()
107
+ defer m .lock .Unlock ()
108
+
109
+ nodeKey := descriptor .FromOCI (node )
110
+ // remove the node from its successors' predecessor list
111
+ for successorKey := range m .successors [nodeKey ] {
112
+ predecessorEntry := m .predecessors [successorKey ]
113
+ predecessorEntry .Delete (nodeKey )
114
+
115
+ // if none of the predecessors of the node still exists, we remove the
116
+ // predecessors entry. Otherwise, we do not remove the entry.
117
+ if len (predecessorEntry ) == 0 {
118
+ delete (m .predecessors , successorKey )
119
+ }
120
+ }
121
+ delete (m .successors , nodeKey )
122
+ delete (m .nodes , nodeKey )
123
+ return nil
124
+ }
125
+
119
126
// index indexes predecessors for each direct successor of the given node.
120
- // There is no data consistency issue as long as deletion is not implemented
121
- // for the underlying storage.
122
- func (m * Memory ) index (ctx context.Context , node ocispec.Descriptor , successors []ocispec.Descriptor ) {
123
- if len (successors ) == 0 {
124
- return
127
+ func (m * Memory ) index (ctx context.Context , fetcher content.Fetcher , node ocispec.Descriptor ) ([]ocispec.Descriptor , error ) {
128
+ successors , err := content .Successors (ctx , fetcher , node )
129
+ if err != nil {
130
+ return nil , err
125
131
}
132
+ m .lock .Lock ()
133
+ defer m .lock .Unlock ()
134
+
135
+ // index the node
136
+ nodeKey := descriptor .FromOCI (node )
137
+ m .nodes [nodeKey ] = node
126
138
127
- predecessorKey := descriptor .FromOCI (node )
139
+ // for each successor, put it into the node's successors list, and
140
+ // put node into the succeesor's predecessors list
141
+ successorSet := set .New [descriptor.Descriptor ]()
142
+ m .successors [nodeKey ] = successorSet
128
143
for _ , successor := range successors {
129
144
successorKey := descriptor .FromOCI (successor )
130
- value , _ := m .predecessors .LoadOrStore (successorKey , & sync.Map {})
131
- predecessors := value .(* sync.Map )
132
- predecessors .Store (predecessorKey , node )
145
+ successorSet .Add (successorKey )
146
+ predecessorSet , exists := m .predecessors [successorKey ]
147
+ if ! exists {
148
+ predecessorSet = set .New [descriptor.Descriptor ]()
149
+ m .predecessors [successorKey ] = predecessorSet
150
+ }
151
+ predecessorSet .Add (nodeKey )
133
152
}
153
+ return successors , nil
134
154
}
0 commit comments