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  }