github.com/leslie-fei/fastcache@v0.0.0-20240520092641-b7a9eb05711f/shard.go (about)

     1  package fastcache
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"unsafe"
     7  )
     8  
     9  type shard struct {
    10  	locker          Locker
    11  	hashmap         *hashMap
    12  	container       *lruAndFreeContainer
    13  	bigContainer    *lruAndFreeContainer
    14  	allocator       *shardAllocator
    15  	globalAllocator *globalAllocator
    16  }
    17  
    18  func (s *shard) Set(hash uint64, key string, value []byte) error {
    19  	locker := s.Locker()
    20  	locker.Lock()
    21  	defer locker.Unlock()
    22  	prevNode, node, el := s.hashmap.FindNode(s.allocator.Base(), hash, key)
    23  	isNew := node == nil
    24  	var err error
    25  	if isNew {
    26  		// not found
    27  		node, err = s.newElement(key, value)
    28  		if err != nil {
    29  			return err
    30  		}
    31  		el = NodeTo[hashMapBucketElement](node)
    32  	} else {
    33  		// found
    34  		total := hashmapElementSize(key, value)
    35  		freeList := s.container.GetIndex(node.FreeBlockIndex)
    36  		blockSize := freeList.Size
    37  		if total > blockSize {
    38  			// larger than old free node size
    39  			// new hashmapElement and delete old node
    40  			oldNode := node
    41  			node, err = s.newElement(key, value)
    42  			if err != nil {
    43  				return err
    44  			}
    45  			_ = s.del(hash, prevNode, oldNode)
    46  			isNew = true
    47  		} else {
    48  			// update data
    49  			el.UpdateValue(value)
    50  		}
    51  	}
    52  
    53  	if isNew {
    54  		s.hashmap.Add(s.allocator.Base(), hash, node)
    55  	}
    56  
    57  	// 插入到LRU list中
    58  	if node.FreeBlockIndex > uint8(SmallFreeListIndex) {
    59  		gLocker := s.globalAllocator.Locker()
    60  		gLocker.Lock()
    61  		s.bigContainer.MoveToFront(s.globalAllocator.Base(), node, &el.lruNode)
    62  		gLocker.Unlock()
    63  	} else {
    64  		s.container.MoveToFront(s.globalAllocator.Base(), node, &el.lruNode)
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  func (s *shard) Get(hash uint64, key string) ([]byte, error) {
    71  	locker := s.Locker()
    72  	locker.RLock()
    73  	defer locker.RUnlock()
    74  	node, value, err := s.hashmap.Get(s.allocator.Base(), hash, key)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	el := NodeTo[hashMapBucketElement](node)
    80  	if node.FreeBlockIndex > uint8(SmallFreeListIndex) {
    81  		gLocker := s.globalAllocator.Locker()
    82  		gLocker.Lock()
    83  		s.bigContainer.MoveToFront(s.globalAllocator.Base(), node, &el.lruNode)
    84  		gLocker.Unlock()
    85  	} else {
    86  		s.container.MoveToFront(s.globalAllocator.Base(), node, &el.lruNode)
    87  	}
    88  
    89  	return value, nil
    90  }
    91  
    92  func (s *shard) Peek(hash uint64, key string) ([]byte, error) {
    93  	locker := s.Locker()
    94  	locker.RLock()
    95  	defer locker.RUnlock()
    96  	_, v, err := s.hashmap.Get(s.allocator.Base(), hash, key)
    97  	return v, err
    98  }
    99  
   100  func (s *shard) Del(hash uint64, key string) (err error) {
   101  	locker := s.Locker()
   102  	locker.Lock()
   103  	defer locker.Unlock()
   104  	prev, node, _ := s.hashmap.FindNode(s.allocator.Base(), hash, key)
   105  	return s.del(hash, prev, node)
   106  }
   107  
   108  func (s *shard) Locker() Locker {
   109  	return s.locker
   110  }
   111  
   112  func (s *shard) del(hash uint64, prev *DataNode, node *DataNode) error {
   113  	err := s.hashmap.Del(s.allocator.Base(), hash, prev, node)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	el := NodeTo[hashMapBucketElement](node)
   119  	if node.FreeBlockIndex > uint8(SmallFreeListIndex) {
   120  		// 说明是来自与bigFreeContainer
   121  		locker := s.globalAllocator.Locker()
   122  		locker.Lock()
   123  		s.bigContainer.Free(s.allocator.Base(), node, &el.lruNode)
   124  		locker.Unlock()
   125  	} else {
   126  		s.container.Free(s.allocator.Base(), node, &el.lruNode)
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  func (s *shard) allocOne(dataSize uint64) (*DataNode, error) {
   133  	if dataSize == 0 {
   134  		return nil, errors.New("data size is zero")
   135  	}
   136  
   137  	index := dataSizeToIndex(dataSize)
   138  	var allocator Allocator
   139  	var container *lruAndFreeContainer
   140  	// 大数据块需要去全局得大数据块bigFreeContainer中获取
   141  	if index > SmallFreeListIndex {
   142  		allocator = s.globalAllocator
   143  		container = s.bigContainer
   144  	} else {
   145  		// 小数据块直接在shard分片中直接分配, 不需要加锁, 这里的allocator Locker是一个nopLocker
   146  		allocator = s.allocator
   147  		container = s.container
   148  	}
   149  	locker := allocator.Locker()
   150  	locker.Lock()
   151  	defer locker.Unlock()
   152  	node, err := container.Alloc(s.globalAllocator, dataSize)
   153  	if err != nil {
   154  		if errors.Is(err, ErrNoSpace) {
   155  			// 触发淘汰
   156  			if err = container.Evict(s.globalAllocator, dataSize); err != nil {
   157  				return nil, err
   158  			}
   159  			// 因为已经触发过淘汰, 这里一定能拿到数据块
   160  			node, err = container.Alloc(s.globalAllocator, dataSize)
   161  			return node, err
   162  		}
   163  		return nil, err
   164  	}
   165  	return node, nil
   166  }
   167  
   168  func (s *shard) newElement(key string, value []byte) (*DataNode, error) {
   169  	total := hashmapElementSize(key, value)
   170  	elNode, err := s.allocOne(total)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	elNode.Len = uint32(total)
   176  	el := NodeTo[hashMapBucketElement](elNode)
   177  	el.keyLen = uint32(len(key))
   178  	el.valLen = uint32(len(value))
   179  
   180  	// set key data
   181  	ss := (*reflect.StringHeader)(unsafe.Pointer(&key))
   182  	keyPtr := uintptr(unsafe.Pointer(el)) + sizeOfHashmapBucketElement
   183  	memmove(unsafe.Pointer(keyPtr), unsafe.Pointer(ss.Data), uintptr(ss.Len))
   184  
   185  	// set value
   186  	valPtr := keyPtr + uintptr(el.keyLen)
   187  	// move value to DataNode data ptr
   188  	bh := (*reflect.SliceHeader)(unsafe.Pointer(&value))
   189  	memmove(unsafe.Pointer(valPtr), unsafe.Pointer(bh.Data), uintptr(len(value)))
   190  
   191  	return elNode, err
   192  }