github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zcache/table.go (about)

     1  package zcache
     2  
     3  import (
     4  	"sort"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/sohaha/zlsgo/ztime"
     9  	"github.com/sohaha/zlsgo/zutil"
    10  	// "github.com/sohaha/zlsgo/zlog"
    11  	// "sync/atomic"
    12  )
    13  
    14  type (
    15  	// CacheItemPair maps key to access counter
    16  	CacheItemPair struct {
    17  		Key         string
    18  		AccessCount int64
    19  	}
    20  	// CacheItemPairList CacheItemPairList
    21  	CacheItemPairList []CacheItemPair
    22  	// Table Table
    23  	Table struct {
    24  		items           map[string]*Item
    25  		cleanupTimer    *time.Timer
    26  		loadNotCallback func(key string, args ...interface{}) *Item
    27  		addCallback     func(item *Item)
    28  		deleteCallback  func(key string) bool
    29  		accessCount     *zutil.Bool
    30  		name            string
    31  		cleanupInterval time.Duration
    32  		sync.RWMutex
    33  	}
    34  )
    35  
    36  // Count get the number of caches
    37  func (table *Table) Count() int {
    38  	table.RLock()
    39  	defer table.RUnlock()
    40  	return len(table.items)
    41  }
    42  
    43  // ForEach traversing the cache
    44  func (table *Table) ForEach(trans func(key string, value interface{}) bool) {
    45  	table.ForEachRaw(func(k string, v *Item) bool {
    46  		return trans(k, v.Data())
    47  	})
    48  }
    49  
    50  // ForEachRaw traversing the cache
    51  func (table *Table) ForEachRaw(trans func(key string, value *Item) bool) {
    52  	count := table.Count()
    53  	table.RLock()
    54  	items := make(map[string]*Item, count)
    55  	for k, v := range table.items {
    56  		items[k] = v
    57  	}
    58  	table.RUnlock()
    59  
    60  	for k, v := range items {
    61  		if !trans(k, v) {
    62  			break
    63  		}
    64  	}
    65  }
    66  
    67  // SetLoadNotCallback SetLoadNotCallback
    68  func (table *Table) SetLoadNotCallback(f func(key string, args ...interface{}) *Item) {
    69  	table.Lock()
    70  	defer table.Unlock()
    71  	table.loadNotCallback = f
    72  }
    73  
    74  // SetAddCallback SetAddCallback
    75  func (table *Table) SetAddCallback(f func(*Item)) {
    76  	table.Lock()
    77  	defer table.Unlock()
    78  	table.addCallback = f
    79  }
    80  
    81  // SetDeleteCallback SetDeleteCallback
    82  func (table *Table) SetDeleteCallback(f func(key string) bool) {
    83  	table.Lock()
    84  	defer table.Unlock()
    85  	table.deleteCallback = f
    86  }
    87  
    88  func (table *Table) expirationCheck() {
    89  	now := ztime.UnixMicro(ztime.Clock())
    90  	smallestDuration := time.Duration(0)
    91  	table.Lock()
    92  	if table.cleanupTimer != nil {
    93  		table.cleanupTimer.Stop()
    94  	}
    95  
    96  	for key, item := range table.items {
    97  		item.RLock()
    98  		lifeSpan := item.lifeSpan
    99  		accessedOn := item.accessedTime
   100  		intervalLifeSpan := item.intervalLifeSpan
   101  		item.RUnlock()
   102  		if lifeSpan == 0 {
   103  			continue
   104  		}
   105  		remainingLift := item.RemainingLife()
   106  		if table.accessCount.Load() && intervalLifeSpan {
   107  			lastTime := now.Sub(accessedOn)
   108  			if lastTime >= lifeSpan {
   109  				_, _ = table.deleteInternal(key)
   110  			} else {
   111  				lifeSpan = lifeSpan * 2
   112  				item.Lock()
   113  				item.lifeSpan = lifeSpan
   114  				item.Unlock()
   115  				nextDuration := lifeSpan - lastTime
   116  				if smallestDuration == 0 || nextDuration < smallestDuration {
   117  					smallestDuration = nextDuration
   118  				}
   119  			}
   120  		} else if remainingLift <= 0 {
   121  			_, _ = table.deleteInternal(key)
   122  		} else {
   123  			if smallestDuration == 0 || smallestDuration > remainingLift {
   124  				smallestDuration = remainingLift
   125  			}
   126  		}
   127  	}
   128  	table.cleanupInterval = smallestDuration
   129  
   130  	if smallestDuration > 0 {
   131  		table.cleanupTimer = time.AfterFunc(smallestDuration, func() {
   132  			go table.expirationCheck()
   133  		})
   134  	}
   135  	table.Unlock()
   136  }
   137  
   138  func (table *Table) addInternal(item *Item) {
   139  	table.Lock()
   140  	table.items[item.key] = item
   141  
   142  	expDur := table.cleanupInterval
   143  	addedItem := table.addCallback
   144  	table.Unlock()
   145  
   146  	if addedItem != nil {
   147  		addedItem(item)
   148  	}
   149  	item.RLock()
   150  	lifeSpan := item.lifeSpan
   151  	item.RUnlock()
   152  	if lifeSpan > 0 && (expDur == 0 || lifeSpan < expDur) {
   153  		go table.expirationCheck()
   154  	}
   155  }
   156  
   157  // SetRaw set cache
   158  func (table *Table) SetRaw(key string, data interface{}, lifeSpan time.Duration,
   159  	intervalLifeSpan ...bool) *Item {
   160  	item := NewCacheItem(key, data, lifeSpan)
   161  
   162  	if len(intervalLifeSpan) > 0 && intervalLifeSpan[0] {
   163  		table.accessCount.Store(true)
   164  		item.intervalLifeSpan = intervalLifeSpan[0]
   165  	}
   166  	table.addInternal(item)
   167  
   168  	return item
   169  }
   170  
   171  // Set set cache whether to automatically renew
   172  func (table *Table) Set(key string, data interface{}, lifeSpanSecond uint,
   173  	interval ...bool) *Item {
   174  	return table.SetRaw(key, data, time.Duration(lifeSpanSecond)*time.Second, interval...)
   175  }
   176  
   177  func (table *Table) deleteInternal(key string) (*Item, error) {
   178  	r, ok := table.items[key]
   179  	if !ok {
   180  		return nil, ErrKeyNotFound
   181  	}
   182  
   183  	deleteCallback := table.deleteCallback
   184  	table.Unlock()
   185  	if deleteCallback != nil && !deleteCallback(r.key) {
   186  		table.Lock()
   187  		r.RLock()
   188  		r.accessedTime = ztime.UnixMicro(ztime.Clock())
   189  		r.RUnlock()
   190  		return r, nil
   191  	}
   192  
   193  	r.RLock()
   194  	defer r.RUnlock()
   195  	if r.deleteCallback != nil && !r.deleteCallback(r.key) {
   196  		table.Lock()
   197  		r.RLock()
   198  		r.accessedTime = ztime.UnixMicro(ztime.Clock())
   199  		r.RUnlock()
   200  		return r, nil
   201  	}
   202  
   203  	table.Lock()
   204  	delete(table.items, key)
   205  	return r, nil
   206  }
   207  
   208  // Delete Delete cache
   209  func (table *Table) Delete(key string) (*Item, error) {
   210  	table.Lock()
   211  	defer table.Unlock()
   212  
   213  	return table.deleteInternal(key)
   214  }
   215  
   216  // Exists Exists
   217  func (table *Table) Exists(key string) bool {
   218  	table.RLock()
   219  	defer table.RUnlock()
   220  	_, ok := table.items[key]
   221  
   222  	return ok
   223  }
   224  
   225  // Add if the cache does not exist then adding does not take effect
   226  func (table *Table) Add(key string, data interface{}, lifeSpan time.Duration, intervalLifeSpan ...bool) bool {
   227  	table.Lock()
   228  	_, ok := table.items[key]
   229  	table.Unlock()
   230  	if ok {
   231  		return false
   232  	}
   233  
   234  	item := NewCacheItem(key, data, lifeSpan)
   235  	if len(intervalLifeSpan) > 0 {
   236  		item.intervalLifeSpan = intervalLifeSpan[0]
   237  	}
   238  	table.addInternal(item)
   239  
   240  	return true
   241  }
   242  
   243  // MustGet get the Raw of the specified key, set if it does not exist
   244  func (table *Table) MustGet(key string, do func(set func(data interface{},
   245  	lifeSpan time.Duration, interval ...bool)) (
   246  	err error)) (data interface{}, err error) {
   247  	table.Lock()
   248  	r, ok := table.items[key]
   249  	if ok {
   250  		table.Unlock()
   251  		r.keepAlive()
   252  		return r.Data(), nil
   253  	}
   254  	item := NewCacheItem(key, "", 0)
   255  	item.Lock()
   256  	table.items[key] = item
   257  	table.Unlock()
   258  	err = do(func(data interface{},
   259  		lifeSpan time.Duration, interval ...bool) {
   260  		item.data = data
   261  		item.lifeSpan = lifeSpan
   262  		if len(interval) > 0 {
   263  			item.intervalLifeSpan = interval[0]
   264  		}
   265  	})
   266  	item.Unlock()
   267  
   268  	if err != nil {
   269  		table.Lock()
   270  		delete(table.items, key)
   271  		table.Unlock()
   272  		return
   273  	}
   274  
   275  	data = item.data
   276  	table.addInternal(item)
   277  	return
   278  }
   279  
   280  // GetT GetT
   281  func (table *Table) GetT(key string, args ...interface{}) (*Item, error) {
   282  	table.RLock()
   283  	r, ok := table.items[key]
   284  	table.RUnlock()
   285  
   286  	if ok {
   287  		if table.accessCount.Load() {
   288  			r.keepAlive()
   289  		}
   290  		return r, nil
   291  	}
   292  
   293  	loadData := table.loadNotCallback
   294  	if loadData != nil {
   295  		item := loadData(key, args...)
   296  		if item != nil {
   297  			table.SetRaw(key, item.data, item.lifeSpan)
   298  			return item, nil
   299  		}
   300  
   301  		return nil, ErrKeyNotFoundAndNotCallback
   302  	}
   303  
   304  	return nil, ErrKeyNotFound
   305  }
   306  
   307  // Get get the Raw of the specified key
   308  func (table *Table) Get(key string, args ...interface{}) (value interface{}, err error) {
   309  	var data *Item
   310  	data, err = table.GetT(key, args...)
   311  	if err != nil {
   312  		return
   313  	}
   314  	value = data.Data()
   315  	return
   316  }
   317  
   318  func (table *Table) GetString(key string, args ...interface{}) (value string, err error) {
   319  	data, err := table.Get(key, args...)
   320  	if err != nil {
   321  		return
   322  	}
   323  	value, _ = data.(string)
   324  	return
   325  }
   326  
   327  func (table *Table) GetInt(key string, args ...interface{}) (value int, err error) {
   328  	data, err := table.Get(key, args...)
   329  	if err != nil {
   330  		return
   331  	}
   332  	value, _ = data.(int)
   333  
   334  	return
   335  }
   336  
   337  // Clear Clear
   338  func (table *Table) Clear() {
   339  	table.Lock()
   340  	table.items = make(map[string]*Item)
   341  	table.cleanupInterval = 0
   342  	if table.cleanupTimer != nil {
   343  		table.cleanupTimer.Stop()
   344  	}
   345  	table.Unlock()
   346  }
   347  
   348  func (p CacheItemPairList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
   349  func (p CacheItemPairList) Len() int           { return len(p) }
   350  func (p CacheItemPairList) Less(i, j int) bool { return p[i].AccessCount > p[j].AccessCount }
   351  
   352  // MostAccessed MostAccessed
   353  func (table *Table) MostAccessed(count int64) []*Item {
   354  	table.RLock()
   355  	defer table.RUnlock()
   356  
   357  	p := make(CacheItemPairList, len(table.items))
   358  	i := 0
   359  	for k, v := range table.items {
   360  		p[i] = CacheItemPair{k, v.accessCount}
   361  		i++
   362  	}
   363  	sort.Sort(p)
   364  
   365  	var r []*Item
   366  	c := int64(0)
   367  	for _, v := range p {
   368  		if c >= count {
   369  			break
   370  		}
   371  
   372  		item, ok := table.items[v.Key]
   373  		if ok {
   374  			r = append(r, item)
   375  		}
   376  		c++
   377  	}
   378  
   379  	return r
   380  }