github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/data/cache/fastcache/timeline.go (about) 1 package fastcache 2 3 import ( 4 "fmt" 5 "github.com/angenalZZZ/gofunc/f" 6 "io/ioutil" 7 "log" 8 "os" 9 "path/filepath" 10 "sync/atomic" 11 "time" 12 ) 13 14 // Timeline time data 15 type Timeline struct { 16 CacheDir string // cache persist to disk directory 17 Frames []*TimeFrame 18 Duration time.Duration 19 Index int64 20 } 21 22 // TimeFrame time bounds on which data to retrieve. 23 type TimeFrame struct { 24 Cache *Cache // a fast thread-safe inmemory cache optimized for big number of entries. 25 Frame *f.TimeFrame 26 Index uint32 27 } 28 29 func (t *Timeline) Write(p []byte) (n int, err error) { 30 if t.Index == -1 { 31 return 32 } 33 34 c := t.Frames[t.Index] 35 i := atomic.AddUint32(&c.Index, 1) 36 c.Cache.Set(f.BytesUint32(i), p) 37 return int(i), nil 38 } 39 40 func (t *Timeline) Save() { 41 for _, frame := range t.Frames { 42 frame.Save(t.CacheDir) 43 } 44 } 45 46 func (t *Timeline) Remove(index int) { 47 if index >= 0 && index < len(t.Frames) { 48 t.Frames[index].Remove(t.CacheDir) 49 } 50 } 51 52 func (t *Timeline) RemoveAll() { 53 for _, frame := range t.Frames { 54 frame.Remove(t.CacheDir) 55 } 56 } 57 58 func (c *TimeFrame) Dirname() string { 59 return fmt.Sprintf("%s.%d", c.Frame.Since.LocalTimeStampString(true), c.Index) 60 } 61 62 func (c *TimeFrame) Filename() string { 63 return fmt.Sprintf("%s.%d.json", c.Frame.Since.LocalTimeStampString(true), c.Index) 64 } 65 66 func (c *TimeFrame) Save(cacheDir string) { 67 time.Sleep(time.Microsecond) 68 if c.Index == 0 { 69 return 70 } 71 72 fileStat := new(Stats) 73 c.Cache.UpdateStats(fileStat) 74 data, err := f.EncodeJson(fileStat) 75 logErr := log.New(os.Stderr, "", 0) 76 if err != nil { 77 logErr.Print(err) 78 } 79 80 filePath := filepath.Join(cacheDir, c.Filename()) 81 err = ioutil.WriteFile(filePath, data, 0644) 82 if err != nil { 83 logErr.Print(err) 84 } 85 86 dirPath := filepath.Join(cacheDir, c.Dirname()) 87 if err = c.Cache.SaveToFileConcurrent(dirPath, 0); err != nil { 88 logErr.Print(err) 89 } else { 90 c.Cache.Reset() // Reset removes all the items from the cache. 91 } 92 } 93 94 func (c *TimeFrame) Remove(cacheDir string) { 95 if c.Index == 0 { 96 return 97 } 98 99 filePath := filepath.Join(cacheDir, c.Dirname()) 100 err := os.Remove(filePath + ".json") 101 logErr := log.New(os.Stderr, "", 0) 102 if err != nil { 103 logErr.Print(err) 104 } 105 106 err = os.RemoveAll(filePath) 107 if err != nil { 108 logErr.Print(err) 109 } 110 } 111 112 func (t *Timeline) init() { 113 p := int64(t.Duration.Seconds()) 114 n := t.Frames[0].Frame.Since.UnixSecond 115 m := t.Frames[len(t.Frames)-1].Frame.Until.UnixSecond 116 for u := time.Now().Unix(); u < m; u++ { 117 index := (u - n) / p 118 if index >= 0 && index != t.Index { 119 if t.Index != -1 { 120 go t.Frames[t.Index].Save(t.CacheDir) 121 } 122 atomic.StoreInt64(&t.Index, index) 123 } 124 time.Sleep(time.Second) 125 } 126 t.Index = -1 127 } 128 129 func NewTimeline(since, until time.Time, duration time.Duration, cacheDir string, maxBytes int) *Timeline { 130 frames := f.NewTimeFrames(since, until, duration) 131 132 t := &Timeline{ 133 CacheDir: cacheDir, 134 Frames: make([]*TimeFrame, len(frames)), 135 Duration: duration, 136 Index: -1, 137 } 138 139 for i, frame := range frames { 140 t.Frames[i] = &TimeFrame{ 141 Cache: New(maxBytes), 142 Frame: frame, 143 } 144 } 145 146 if len(t.Frames) > 0 { 147 go t.init() 148 // wait init step 149 if since.Before(time.Now()) { 150 time.Sleep(time.Microsecond) 151 } 152 } 153 154 return t 155 }