github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/go-xorm/xorm/lru_cacher.go (about)

     1  // Copyright 2015 The Xorm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package xorm
     6  
     7  import (
     8  	"container/list"
     9  	"fmt"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/insionng/yougam/libraries/go-xorm/core"
    14  )
    15  
    16  type LRUCacher struct {
    17  	idList   *list.List
    18  	sqlList  *list.List
    19  	idIndex  map[string]map[string]*list.Element
    20  	sqlIndex map[string]map[string]*list.Element
    21  	store    core.CacheStore
    22  	mutex    sync.Mutex
    23  	// maxSize    int
    24  	MaxElementSize int
    25  	Expired        time.Duration
    26  	GcInterval     time.Duration
    27  }
    28  
    29  func NewLRUCacher(store core.CacheStore, maxElementSize int) *LRUCacher {
    30  	return NewLRUCacher2(store, 3600*time.Second, maxElementSize)
    31  }
    32  
    33  func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize int) *LRUCacher {
    34  	cacher := &LRUCacher{store: store, idList: list.New(),
    35  		sqlList: list.New(), Expired: expired,
    36  		GcInterval: core.CacheGcInterval, MaxElementSize: maxElementSize,
    37  		sqlIndex: make(map[string]map[string]*list.Element),
    38  		idIndex:  make(map[string]map[string]*list.Element),
    39  	}
    40  	cacher.RunGC()
    41  	return cacher
    42  }
    43  
    44  //func NewLRUCacher3(store CacheStore, expired time.Duration, maxSize int) *LRUCacher {
    45  //    return newLRUCacher(store, expired, maxSize, 0)
    46  //}
    47  
    48  // RunGC run once every m.GcInterval
    49  func (m *LRUCacher) RunGC() {
    50  	time.AfterFunc(m.GcInterval, func() {
    51  		m.RunGC()
    52  		m.GC()
    53  	})
    54  }
    55  
    56  // GC check ids lit and sql list to remove all element expired
    57  func (m *LRUCacher) GC() {
    58  	//fmt.Println("begin gc ...")
    59  	//defer fmt.Println("end gc ...")
    60  	m.mutex.Lock()
    61  	defer m.mutex.Unlock()
    62  	var removedNum int
    63  	for e := m.idList.Front(); e != nil; {
    64  		if removedNum <= core.CacheGcMaxRemoved &&
    65  			time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired {
    66  			removedNum++
    67  			next := e.Next()
    68  			//fmt.Println("removing ...", e.Value)
    69  			node := e.Value.(*idNode)
    70  			m.delBean(node.tbName, node.id)
    71  			e = next
    72  		} else {
    73  			//fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.idList.Len())
    74  			break
    75  		}
    76  	}
    77  
    78  	removedNum = 0
    79  	for e := m.sqlList.Front(); e != nil; {
    80  		if removedNum <= core.CacheGcMaxRemoved &&
    81  			time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired {
    82  			removedNum++
    83  			next := e.Next()
    84  			//fmt.Println("removing ...", e.Value)
    85  			node := e.Value.(*sqlNode)
    86  			m.delIds(node.tbName, node.sql)
    87  			e = next
    88  		} else {
    89  			//fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.sqlList.Len())
    90  			break
    91  		}
    92  	}
    93  }
    94  
    95  // GetIds returns all bean's ids according to sql and parameter from cache
    96  func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
    97  	m.mutex.Lock()
    98  	defer m.mutex.Unlock()
    99  	if _, ok := m.sqlIndex[tableName]; !ok {
   100  		m.sqlIndex[tableName] = make(map[string]*list.Element)
   101  	}
   102  	if v, err := m.store.Get(sql); err == nil {
   103  		if el, ok := m.sqlIndex[tableName][sql]; !ok {
   104  			el = m.sqlList.PushBack(newSqlNode(tableName, sql))
   105  			m.sqlIndex[tableName][sql] = el
   106  		} else {
   107  			lastTime := el.Value.(*sqlNode).lastVisit
   108  			// if expired, remove the node and return nil
   109  			if time.Now().Sub(lastTime) > m.Expired {
   110  				m.delIds(tableName, sql)
   111  				return nil
   112  			}
   113  			m.sqlList.MoveToBack(el)
   114  			el.Value.(*sqlNode).lastVisit = time.Now()
   115  		}
   116  		return v
   117  	} else {
   118  		m.delIds(tableName, sql)
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  // GetBean returns bean according tableName and id from cache
   125  func (m *LRUCacher) GetBean(tableName string, id string) interface{} {
   126  	m.mutex.Lock()
   127  	defer m.mutex.Unlock()
   128  	if _, ok := m.idIndex[tableName]; !ok {
   129  		m.idIndex[tableName] = make(map[string]*list.Element)
   130  	}
   131  	tid := genId(tableName, id)
   132  	if v, err := m.store.Get(tid); err == nil {
   133  		if el, ok := m.idIndex[tableName][id]; ok {
   134  			lastTime := el.Value.(*idNode).lastVisit
   135  			// if expired, remove the node and return nil
   136  			if time.Now().Sub(lastTime) > m.Expired {
   137  				m.delBean(tableName, id)
   138  				//m.clearIds(tableName)
   139  				return nil
   140  			}
   141  			m.idList.MoveToBack(el)
   142  			el.Value.(*idNode).lastVisit = time.Now()
   143  		} else {
   144  			el = m.idList.PushBack(newIdNode(tableName, id))
   145  			m.idIndex[tableName][id] = el
   146  		}
   147  		return v
   148  	} else {
   149  		// store bean is not exist, then remove memory's index
   150  		m.delBean(tableName, id)
   151  		//m.clearIds(tableName)
   152  		return nil
   153  	}
   154  }
   155  
   156  // Clear all sql-ids mapping on table tableName from cache
   157  func (m *LRUCacher) clearIds(tableName string) {
   158  	if tis, ok := m.sqlIndex[tableName]; ok {
   159  		for sql, v := range tis {
   160  			m.sqlList.Remove(v)
   161  			m.store.Del(sql)
   162  		}
   163  	}
   164  	m.sqlIndex[tableName] = make(map[string]*list.Element)
   165  }
   166  
   167  func (m *LRUCacher) ClearIds(tableName string) {
   168  	m.mutex.Lock()
   169  	defer m.mutex.Unlock()
   170  	m.clearIds(tableName)
   171  }
   172  
   173  func (m *LRUCacher) clearBeans(tableName string) {
   174  	if tis, ok := m.idIndex[tableName]; ok {
   175  		for id, v := range tis {
   176  			m.idList.Remove(v)
   177  			tid := genId(tableName, id)
   178  			m.store.Del(tid)
   179  		}
   180  	}
   181  	m.idIndex[tableName] = make(map[string]*list.Element)
   182  }
   183  
   184  func (m *LRUCacher) ClearBeans(tableName string) {
   185  	m.mutex.Lock()
   186  	defer m.mutex.Unlock()
   187  	m.clearBeans(tableName)
   188  }
   189  
   190  func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
   191  	m.mutex.Lock()
   192  	defer m.mutex.Unlock()
   193  	if _, ok := m.sqlIndex[tableName]; !ok {
   194  		m.sqlIndex[tableName] = make(map[string]*list.Element)
   195  	}
   196  	if el, ok := m.sqlIndex[tableName][sql]; !ok {
   197  		el = m.sqlList.PushBack(newSqlNode(tableName, sql))
   198  		m.sqlIndex[tableName][sql] = el
   199  	} else {
   200  		el.Value.(*sqlNode).lastVisit = time.Now()
   201  	}
   202  	m.store.Put(sql, ids)
   203  	if m.sqlList.Len() > m.MaxElementSize {
   204  		e := m.sqlList.Front()
   205  		node := e.Value.(*sqlNode)
   206  		m.delIds(node.tbName, node.sql)
   207  	}
   208  }
   209  
   210  func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
   211  	m.mutex.Lock()
   212  	defer m.mutex.Unlock()
   213  	var el *list.Element
   214  	var ok bool
   215  
   216  	if el, ok = m.idIndex[tableName][id]; !ok {
   217  		el = m.idList.PushBack(newIdNode(tableName, id))
   218  		m.idIndex[tableName][id] = el
   219  	} else {
   220  		el.Value.(*idNode).lastVisit = time.Now()
   221  	}
   222  
   223  	m.store.Put(genId(tableName, id), obj)
   224  	if m.idList.Len() > m.MaxElementSize {
   225  		e := m.idList.Front()
   226  		node := e.Value.(*idNode)
   227  		m.delBean(node.tbName, node.id)
   228  	}
   229  }
   230  
   231  func (m *LRUCacher) delIds(tableName, sql string) {
   232  	if _, ok := m.sqlIndex[tableName]; ok {
   233  		if el, ok := m.sqlIndex[tableName][sql]; ok {
   234  			delete(m.sqlIndex[tableName], sql)
   235  			m.sqlList.Remove(el)
   236  		}
   237  	}
   238  	m.store.Del(sql)
   239  }
   240  
   241  func (m *LRUCacher) DelIds(tableName, sql string) {
   242  	m.mutex.Lock()
   243  	defer m.mutex.Unlock()
   244  	m.delIds(tableName, sql)
   245  }
   246  
   247  func (m *LRUCacher) delBean(tableName string, id string) {
   248  	tid := genId(tableName, id)
   249  	if el, ok := m.idIndex[tableName][id]; ok {
   250  		delete(m.idIndex[tableName], id)
   251  		m.idList.Remove(el)
   252  		m.clearIds(tableName)
   253  	}
   254  	m.store.Del(tid)
   255  }
   256  
   257  func (m *LRUCacher) DelBean(tableName string, id string) {
   258  	m.mutex.Lock()
   259  	defer m.mutex.Unlock()
   260  	m.delBean(tableName, id)
   261  }
   262  
   263  type idNode struct {
   264  	tbName    string
   265  	id        string
   266  	lastVisit time.Time
   267  }
   268  
   269  type sqlNode struct {
   270  	tbName    string
   271  	sql       string
   272  	lastVisit time.Time
   273  }
   274  
   275  func genSqlKey(sql string, args interface{}) string {
   276  	return fmt.Sprintf("%v-%v", sql, args)
   277  }
   278  
   279  func genId(prefix string, id string) string {
   280  	return fmt.Sprintf("%v-%v", prefix, id)
   281  }
   282  
   283  func newIdNode(tbName string, id string) *idNode {
   284  	return &idNode{tbName, id, time.Now()}
   285  }
   286  
   287  func newSqlNode(tbName, sql string) *sqlNode {
   288  	return &sqlNode{tbName, sql, time.Now()}
   289  }