@@ -23,68 +23,54 @@ import (
2323 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2424 "oras.land/oras-go/v2/content"
2525 "oras.land/oras-go/v2/errdef"
26+ "oras.land/oras-go/v2/internal/container/set"
2627 "oras.land/oras-go/v2/internal/descriptor"
2728 "oras.land/oras-go/v2/internal/status"
2829 "oras.land/oras-go/v2/internal/syncutil"
2930)
3031
3132// Memory is a memory based PredecessorFinder.
3233type 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
3538}
3639
3740// NewMemory creates a new memory PredecessorFinder.
3841func 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+ }
4047}
4148
4249// 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.
4550func (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
5353}
5454
5555// 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.
5856func (m * Memory ) IndexAll (ctx context.Context , fetcher content.Fetcher , node ocispec.Descriptor ) error {
5957 // track content status
6058 tracker := status .NewTracker ()
61-
6259 var fn syncutil.GoFunc [ocispec.Descriptor ]
6360 fn = func (ctx context.Context , region * syncutil.LimitedRegion , desc ocispec.Descriptor ) error {
6461 // skip the node if other go routine is working on it
6562 _ , committed := tracker .TryCommit (desc )
6663 if ! committed {
6764 return nil
6865 }
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 )
7867 if err != nil {
7968 if errors .Is (err , errdef .ErrNotFound ) {
8069 // skip the node if it does not exist
8170 return nil
8271 }
8372 return err
8473 }
85- m .index (ctx , desc , successors )
86- m .indexed .Store (key , nil )
87-
8874 if len (successors ) > 0 {
8975 // traverse and index successors
9076 return syncutil .Go (ctx , nil , fn , successors ... )
@@ -96,39 +82,73 @@ func (m *Memory) IndexAll(ctx context.Context, fetcher content.Fetcher, node oci
9682
9783// Predecessors returns the nodes directly pointing to the current node.
9884// 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.
10388func (m * Memory ) Predecessors (_ context.Context , node ocispec.Descriptor ) ([]ocispec.Descriptor , error ) {
89+ m .lock .RLock ()
90+ defer m .lock .RUnlock ()
91+
10492 key := descriptor .FromOCI (node )
105- value , exists := m .predecessors . Load ( key )
93+ set , exists := m .predecessors [ key ]
10694 if ! exists {
10795 return nil , nil
10896 }
109- predecessors := value .(* sync.Map )
110-
11197 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+ }
116101 return res , nil
117102}
118103
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+
119126// 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
125131 }
132+ m .lock .Lock ()
133+ defer m .lock .Unlock ()
134+
135+ // index the node
136+ nodeKey := descriptor .FromOCI (node )
137+ m .nodes [nodeKey ] = node
126138
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
128143 for _ , successor := range successors {
129144 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 )
133152 }
153+ return successors , nil
134154}
0 commit comments