func (c *TTLString) PutWithTTL(key string, value interface{}, ttl time.Duration) {
	c.ttlCache.putWithTTL(key, value, ttl)
}
PutWithTTL puts an item into cache with specified TTL.
package cache
import (
	"context"
	"sync"
	"time"
	"github.com/pingcap/log"
	"go.uber.org/zap"
)
type ttlCacheItem struct {
	value  interface{}
	expire time.Time
}
type ttlCache struct {
	sync.RWMutex
	ctx context.Context
	items      map[interface{}]ttlCacheItem
	ttl        time.Duration
	gcInterval time.Duration
}
func newTTL(ctx context.Context, gcInterval time.Duration, duration time.Duration) *ttlCache {
	c := &ttlCache{
		ctx:        ctx,
		items:      make(map[interface{}]ttlCacheItem),
		ttl:        duration,
		gcInterval: gcInterval,
	}
	go c.doGC()
	return c
}
func (c *ttlCache) put(key interface{}, value interface{}) {
	c.putWithTTL(key, value, c.ttl)
}
func (c *ttlCache) putWithTTL(key interface{}, value interface{}, ttl time.Duration) {
	c.Lock()
	defer c.Unlock()
	c.items[key] = ttlCacheItem{
		value:  value,
		expire: time.Now().Add(ttl),
	}
}
func (c *ttlCache) get(key interface{}) (interface{}, bool) {
	c.RLock()
	defer c.RUnlock()
	item, ok := c.items[key]
	if !ok {
		return nil, false
	}
	if item.expire.Before(time.Now()) {
		return nil, false
	}
	return item.value, true
}
func (c *ttlCache) getKeys() []interface{} {
	c.RLock()
	defer c.RUnlock()
	var keys []interface{}
	now := time.Now()
	for key, item := range c.items {
		if item.expire.After(now) {
			keys = append(keys, key)
		}
	}
	return keys
}
func (c *ttlCache) remove(key interface{}) {
	c.Lock()
	defer c.Unlock()
	delete(c.items, key)
}
func (c *ttlCache) pop() (interface{}, interface{}, bool) {
	c.Lock()
	defer c.Unlock()
	now := time.Now()
	for k, item := range c.items {
		if item.expire.After(now) {
			value := item.value
			delete(c.items, k)
			return k, value, true
		}
	}
	return nil, nil, false
}
func (c *ttlCache) Len() int {
	c.RLock()
	defer c.RUnlock()
	return len(c.items)
}
func (c *ttlCache) Clear() {
	c.Lock()
	defer c.Unlock()
	for k := range c.items {
		delete(c.items, k)
	}
}
func (c *ttlCache) doGC() {
	ticker := time.NewTicker(c.gcInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			count := 0
			now := time.Now()
			c.Lock()
			for key := range c.items {
				if value, ok := c.items[key]; ok {
					if value.expire.Before(now) {
						count++
						delete(c.items, key)
					}
				}
			}
			c.Unlock()
			log.Debug("TTL GC items", zap.Int("count", count))
		case <-c.ctx.Done():
			return
		}
	}
}
type TTLUint64 struct {
	*ttlCache
}
func NewIDTTL(ctx context.Context, gcInterval, ttl time.Duration) *TTLUint64 {
	return &TTLUint64{
		ttlCache: newTTL(ctx, gcInterval, ttl),
	}
}
func (c *TTLUint64) Get(id uint64) (interface{}, bool) {
	return c.ttlCache.get(id)
}
func (c *TTLUint64) Put(id uint64, value interface{}) {
	c.ttlCache.put(id, value)
}
func (c *TTLUint64) GetAllID() []uint64 {
	keys := c.ttlCache.getKeys()
	var ids []uint64
	for _, key := range keys {
		id, ok := key.(uint64)
		if ok {
			ids = append(ids, id)
		}
	}
	return ids
}
func (c *TTLUint64) Exists(id uint64) bool {
	_, ok := c.ttlCache.get(id)
	return ok
}
func (c *TTLUint64) Remove(key uint64) {
	c.ttlCache.remove(key)
}
func (c *TTLUint64) PutWithTTL(key uint64, value interface{}, ttl time.Duration) {
	c.ttlCache.putWithTTL(key, value, ttl)
}
type TTLString struct {
	*ttlCache
}
func NewStringTTL(ctx context.Context, gcInterval, ttl time.Duration) *TTLString {
	return &TTLString{
		ttlCache: newTTL(ctx, gcInterval, ttl),
	}
}
func (c *TTLString) Put(key string, value interface{}) {
	c.ttlCache.put(key, value)
}