99 "fmt"
1010 "strconv"
1111 "strings"
12+ "sync"
1213 "time"
1314
1415 "gitlab.com/postgres-ai/database-lab/pkg/log"
@@ -27,6 +28,7 @@ type baseCloning struct {
2728 cloning
2829
2930 // TODO(akartasov): Fix data race.
31+ cloneMutex sync.RWMutex
3032 clones map [string ]* CloneWrapper
3133 instanceStatus * models.InstanceStatus
3234 snapshots []* models.Snapshot
@@ -67,7 +69,7 @@ func (c *baseCloning) CreateClone(clone *models.Clone) error {
6769 // TODO(akartasov): Separate validation rules.
6870 clone .ID = strings .TrimSpace (clone .ID )
6971
70- if _ , ok := c .clones [ clone .ID ] ; ok {
72+ if _ , ok := c .findWrapper ( clone .ID ) ; ok {
7173 return errors .New ("clone with such ID already exists" )
7274 }
7375
@@ -88,7 +90,6 @@ func (c *baseCloning) CreateClone(clone *models.Clone) error {
8890 }
8991
9092 w := NewCloneWrapper (clone )
91- c .clones [clone .ID ] = w
9293
9394 clone .Status = & models.Status {
9495 Code : models .StatusCreating ,
@@ -153,11 +154,13 @@ func (c *baseCloning) CreateClone(clone *models.Clone) error {
153154 }
154155 }()
155156
157+ c .setWrapper (clone .ID , w )
158+
156159 return nil
157160}
158161
159162func (c * baseCloning ) DestroyClone (id string ) error {
160- w , ok := c .clones [ id ]
163+ w , ok := c .findWrapper ( id )
161164 if ! ok {
162165 return errors .New ("clone not found" )
163166 }
@@ -187,14 +190,16 @@ func (c *baseCloning) DestroyClone(id string) error {
187190 return
188191 }
189192
193+ c .cloneMutex .Lock ()
190194 delete (c .clones , w .clone .ID )
195+ c .cloneMutex .Unlock ()
191196 }()
192197
193198 return nil
194199}
195200
196201func (c * baseCloning ) GetClone (id string ) (* models.Clone , error ) {
197- w , ok := c .clones [ id ]
202+ w , ok := c .findWrapper ( id )
198203 if ! ok {
199204 return nil , errors .New ("clone not found" )
200205 }
@@ -249,20 +254,22 @@ func (c *baseCloning) UpdateClone(id string, patch *models.Clone) error {
249254 return errors .New ("CreatedAt cannot be changed" )
250255 }
251256
252- w , ok := c .clones [ id ]
257+ w , ok := c .findWrapper ( id )
253258 if ! ok {
254259 return errors .New ("clone not found" )
255260 }
256261
257262 // Set fields.
258263
264+ c .cloneMutex .Lock ()
259265 w .clone .Protected = patch .Protected
266+ c .cloneMutex .Unlock ()
260267
261268 return nil
262269}
263270
264271func (c * baseCloning ) ResetClone (id string ) error {
265- w , ok := c .clones [ id ]
272+ w , ok := c .findWrapper ( id )
266273 if ! ok {
267274 return errors .New ("clone not found" )
268275 }
@@ -328,15 +335,32 @@ func (c *baseCloning) GetSnapshots() ([]*models.Snapshot, error) {
328335 return c .snapshots , nil
329336}
330337
338+ // GetClones returns all clones.
331339func (c * baseCloning ) GetClones () []* models.Clone {
332- clones := make ([]* models.Clone , 0 )
340+ clones := make ([]* models.Clone , 0 , len ( c . clones ) )
333341 for _ , clone := range c .clones {
334342 clones = append (clones , clone .clone )
335343 }
336344
337345 return clones
338346}
339347
348+ // findWrapper retrieves a clone findWrapper by id.
349+ func (c * baseCloning ) findWrapper (id string ) (* CloneWrapper , bool ) {
350+ c .cloneMutex .RLock ()
351+ w , ok := c .clones [id ]
352+ c .cloneMutex .RUnlock ()
353+
354+ return w , ok
355+ }
356+
357+ // setWrapper adds a clone wrapper to the map of clones.
358+ func (c * baseCloning ) setWrapper (id string , wrapper * CloneWrapper ) {
359+ c .cloneMutex .Lock ()
360+ c .clones [id ] = wrapper
361+ c .cloneMutex .Unlock ()
362+ }
363+
340364func (c * baseCloning ) getExpectedCloningTime () float64 {
341365 if len (c .clones ) == 0 {
342366 return 0
@@ -432,6 +456,11 @@ func (c *baseCloning) isIdleClone(wrapper *CloneWrapper) (bool, error) {
432456
433457 session := wrapper .session
434458
459+ // TODO(akartasov): Remove wrappers without session.
460+ if session == nil {
461+ return false , errors .New ("failed to get clone session" )
462+ }
463+
435464 lastSessionActivity , err := c .provision .LastSessionActivity (session , idleDuration )
436465 if err != nil {
437466 if err == pglog .ErrNotFound {
0 commit comments