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 }