progress_cache.go
⎇
Raw
1package service
2
3import (
4 "strings"
5 "sync"
6)
7
8type LiveDownload struct {
9 Logs strings.Builder
10 flushed int // length of Logs already persisted to the DB
11}
12
13type ProgressCache struct {
14 mu sync.RWMutex
15 data map[int64]*LiveDownload
16}
17
18func NewProgressCache() *ProgressCache {
19 return &ProgressCache{
20 data: make(map[int64]*LiveDownload),
21 }
22}
23
24func (c *ProgressCache) Set(id int64, d *LiveDownload) {
25 c.mu.Lock()
26 defer c.mu.Unlock()
27 c.data[id] = d
28}
29
30func (c *ProgressCache) AppendLog(id int64, line string) {
31 c.mu.Lock()
32 defer c.mu.Unlock()
33 if d, ok := c.data[id]; ok {
34 d.Logs.WriteString(line)
35 d.Logs.WriteByte('\n')
36 }
37}
38
39// Snapshot returns the full in-memory log buffer for a live download, read under
40// the lock so it never races the writer in AppendLog (strings.Builder is not
41// safe for concurrent read/write). Empty string if the download isn't live.
42func (c *ProgressCache) Snapshot(id int64) string {
43 c.mu.RLock()
44 defer c.mu.RUnlock()
45 if d, ok := c.data[id]; ok {
46 return d.Logs.String()
47 }
48 return ""
49}
50
51func (c *ProgressCache) Delete(id int64) {
52 c.mu.Lock()
53 defer c.mu.Unlock()
54 delete(c.data, id)
55}
56
57// FlushLogs returns only the log content appended since the last flush and
58// advances the flushed mark, so the caller can append (rather than rewrite the
59// whole buffer) to the DB. Empty string when there's nothing new.
60func (c *ProgressCache) FlushLogs(id int64) string {
61 c.mu.Lock()
62 defer c.mu.Unlock()
63 d, ok := c.data[id]
64 if !ok {
65 return ""
66 }
67 full := d.Logs.String()
68 if d.flushed >= len(full) {
69 return ""
70 }
71 tail := full[d.flushed:]
72 d.flushed = len(full)
73 return tail
74}
75