forked from folbricht/desync
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwritededupqueue.go
More file actions
74 lines (60 loc) · 2.19 KB
/
Copy pathwritededupqueue.go
File metadata and controls
74 lines (60 loc) · 2.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package desync
import "fmt"
var _ WriteStore = &WriteDedupQueue{}
// WriteDedupQueue wraps a writable store and provides deduplication of incoming chunk requests and store
// operation. This is useful when a burst of requests for the same chunk is received and the chunk store
// serving those is slow or when the underlying filesystem does not support atomic rename operations
// (Windows). With the DedupQueue wrapper, concurrent requests for the same chunk will result in just
// one request to the upstream store. Implements the WriteStore interface.
type WriteDedupQueue struct {
S WriteStore
*DedupQueue
storeChunkQueue *queue
}
// NewWriteDedupQueue initializes a new instance of the wrapper.
func NewWriteDedupQueue(store WriteStore) *WriteDedupQueue {
return &WriteDedupQueue{
S: store,
DedupQueue: NewDedupQueue(store),
storeChunkQueue: newQueue(),
}
}
func (q *WriteDedupQueue) GetChunk(id ChunkID) (*Chunk, error) {
// If the chunk is being stored just wait and return the data
q.storeChunkQueue.mu.Lock()
req, isInFlight := q.storeChunkQueue.requests[id]
q.storeChunkQueue.mu.Unlock()
if isInFlight {
data, err := req.wait()
switch b := data.(type) {
case nil:
return nil, err
case *Chunk:
return b, err
default:
return nil, fmt.Errorf("internal error: unexpected type %T", data)
}
}
// If the chunk is not currently being stored get the chunk as usual
return q.DedupQueue.GetChunk(id)
}
func (q *WriteDedupQueue) HasChunk(id ChunkID) (bool, error) {
return q.DedupQueue.HasChunk(id)
}
func (q *WriteDedupQueue) StoreChunk(chunk *Chunk) error {
id := chunk.ID()
req, isInFlight := q.storeChunkQueue.loadOrStore(id)
if isInFlight { // The request is already in-flight, wait for it to come back
_, err := req.wait()
return err
}
// This request is the first one for this chunk, execute as normal
err := q.S.StoreChunk(chunk)
// Signal to any others that wait for us that we're done, they'll use our data
// and don't need to hit the store themselves
req.markDone(chunk, err)
// We're done, drop the request from the queue to avoid keeping all the chunk data
// in memory after the request is done
q.storeChunkQueue.delete(id)
return err
}