Browse code

Sequence non-persistent objects in cache

Since the datastore interface is common for persistent and
non-persistent objects we need to provide the same kind of sequencing
and atomicity guarantess to non-persistent data operations as we do for
persistent operations. So added sequencing and atomicity checks in the
data cache layer.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>

Jana Radhakrishnan authored on 2016/06/14 15:53:05
Showing 2 changed files
... ...
@@ -86,25 +86,52 @@ out:
86 86
 	return kmap, nil
87 87
 }
88 88
 
89
-func (c *cache) add(kvObject KVObject) error {
89
+func (c *cache) add(kvObject KVObject, atomic bool) error {
90 90
 	kmap, err := c.kmap(kvObject)
91 91
 	if err != nil {
92 92
 		return err
93 93
 	}
94 94
 
95 95
 	c.Lock()
96
+	// If atomic is true, cache needs to maintain its own index
97
+	// for atomicity and the add needs to be atomic.
98
+	if atomic {
99
+		if prev, ok := kmap[Key(kvObject.Key()...)]; ok {
100
+			if prev.Index() != kvObject.Index() {
101
+				c.Unlock()
102
+				return ErrKeyModified
103
+			}
104
+		}
105
+
106
+		// Increment index
107
+		index := kvObject.Index()
108
+		index++
109
+		kvObject.SetIndex(index)
110
+	}
111
+
96 112
 	kmap[Key(kvObject.Key()...)] = kvObject
97 113
 	c.Unlock()
98 114
 	return nil
99 115
 }
100 116
 
101
-func (c *cache) del(kvObject KVObject) error {
117
+func (c *cache) del(kvObject KVObject, atomic bool) error {
102 118
 	kmap, err := c.kmap(kvObject)
103 119
 	if err != nil {
104 120
 		return err
105 121
 	}
106 122
 
107 123
 	c.Lock()
124
+	// If atomic is true, cache needs to maintain its own index
125
+	// for atomicity and del needs to be atomic.
126
+	if atomic {
127
+		if prev, ok := kmap[Key(kvObject.Key()...)]; ok {
128
+			if prev.Index() != kvObject.Index() {
129
+				c.Unlock()
130
+				return ErrKeyModified
131
+			}
132
+		}
133
+	}
134
+
108 135
 	delete(kmap, Key(kvObject.Key()...))
109 136
 	c.Unlock()
110 137
 	return nil
... ...
@@ -410,7 +410,9 @@ func (ds *datastore) PutObjectAtomic(kvObject KVObject) error {
410 410
 
411 411
 add_cache:
412 412
 	if ds.cache != nil {
413
-		return ds.cache.add(kvObject)
413
+		// If persistent store is skipped, sequencing needs to
414
+		// happen in cache.
415
+		return ds.cache.add(kvObject, kvObject.Skip())
414 416
 	}
415 417
 
416 418
 	return nil
... ...
@@ -435,7 +437,9 @@ func (ds *datastore) PutObject(kvObject KVObject) error {
435 435
 
436 436
 add_cache:
437 437
 	if ds.cache != nil {
438
-		return ds.cache.add(kvObject)
438
+		// If persistent store is skipped, sequencing needs to
439
+		// happen in cache.
440
+		return ds.cache.add(kvObject, kvObject.Skip())
439 441
 	}
440 442
 
441 443
 	return nil
... ...
@@ -537,7 +541,9 @@ func (ds *datastore) DeleteObject(kvObject KVObject) error {
537 537
 
538 538
 	// cleaup the cache first
539 539
 	if ds.cache != nil {
540
-		ds.cache.del(kvObject)
540
+		// If persistent store is skipped, sequencing needs to
541
+		// happen in cache.
542
+		ds.cache.del(kvObject, kvObject.Skip())
541 543
 	}
542 544
 
543 545
 	if kvObject.Skip() {
... ...
@@ -572,7 +578,9 @@ func (ds *datastore) DeleteObjectAtomic(kvObject KVObject) error {
572 572
 del_cache:
573 573
 	// cleanup the cache only if AtomicDelete went through successfully
574 574
 	if ds.cache != nil {
575
-		return ds.cache.del(kvObject)
575
+		// If persistent store is skipped, sequencing needs to
576
+		// happen in cache.
577
+		return ds.cache.del(kvObject, kvObject.Skip())
576 578
 	}
577 579
 
578 580
 	return nil
... ...
@@ -585,7 +593,9 @@ func (ds *datastore) DeleteTree(kvObject KVObject) error {
585 585
 
586 586
 	// cleaup the cache first
587 587
 	if ds.cache != nil {
588
-		ds.cache.del(kvObject)
588
+		// If persistent store is skipped, sequencing needs to
589
+		// happen in cache.
590
+		ds.cache.del(kvObject, kvObject.Skip())
589 591
 	}
590 592
 
591 593
 	if kvObject.Skip() {