github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/list_file_hash_map_sqlite.go (about) 1 // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . 2 3 package caches 4 5 import ( 6 memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem" 7 "github.com/TeaOSLab/EdgeNode/internal/zero" 8 "math/big" 9 "sync" 10 ) 11 12 const HashMapSharding = 31 13 14 var bigIntPool = sync.Pool{ 15 New: func() any { 16 return big.NewInt(0) 17 }, 18 } 19 20 // SQLiteFileListHashMap 文件Hash列表 21 type SQLiteFileListHashMap struct { 22 m []map[uint64]zero.Zero 23 24 lockers []*sync.RWMutex 25 26 isAvailable bool 27 isReady bool 28 } 29 30 func NewSQLiteFileListHashMap() *SQLiteFileListHashMap { 31 var m = make([]map[uint64]zero.Zero, HashMapSharding) 32 var lockers = make([]*sync.RWMutex, HashMapSharding) 33 34 for i := 0; i < HashMapSharding; i++ { 35 m[i] = map[uint64]zero.Zero{} 36 lockers[i] = &sync.RWMutex{} 37 } 38 39 return &SQLiteFileListHashMap{ 40 m: m, 41 lockers: lockers, 42 isAvailable: false, 43 isReady: false, 44 } 45 } 46 47 func (this *SQLiteFileListHashMap) Load(db *SQLiteFileListDB) error { 48 // 如果系统内存过小,我们不缓存 49 if memutils.SystemMemoryGB() < 3 { 50 return nil 51 } 52 53 this.isAvailable = true 54 55 var lastId int64 56 var maxLoops = 50_000 57 for { 58 hashList, maxId, err := db.ListHashes(lastId) 59 if err != nil { 60 return err 61 } 62 if len(hashList) == 0 { 63 break 64 } 65 this.AddHashes(hashList) 66 lastId = maxId 67 68 maxLoops-- 69 if maxLoops <= 0 { 70 break 71 } 72 } 73 74 this.isReady = true 75 return nil 76 } 77 78 func (this *SQLiteFileListHashMap) Add(hash string) { 79 if !this.isAvailable { 80 return 81 } 82 83 hashInt, index := this.bigInt(hash) 84 85 this.lockers[index].Lock() 86 this.m[index][hashInt] = zero.New() 87 this.lockers[index].Unlock() 88 } 89 90 func (this *SQLiteFileListHashMap) AddHashes(hashes []string) { 91 if !this.isAvailable { 92 return 93 } 94 95 for _, hash := range hashes { 96 hashInt, index := this.bigInt(hash) 97 this.lockers[index].Lock() 98 this.m[index][hashInt] = zero.New() 99 this.lockers[index].Unlock() 100 } 101 } 102 103 func (this *SQLiteFileListHashMap) Delete(hash string) { 104 if !this.isAvailable { 105 return 106 } 107 108 hashInt, index := this.bigInt(hash) 109 this.lockers[index].Lock() 110 delete(this.m[index], hashInt) 111 this.lockers[index].Unlock() 112 } 113 114 func (this *SQLiteFileListHashMap) Exist(hash string) bool { 115 if !this.isAvailable { 116 return true 117 } 118 if !this.isReady { 119 // 只有完全Ready时才能判断是否为false 120 return true 121 } 122 123 hashInt, index := this.bigInt(hash) 124 125 this.lockers[index].RLock() 126 _, ok := this.m[index][hashInt] 127 this.lockers[index].RUnlock() 128 return ok 129 } 130 131 func (this *SQLiteFileListHashMap) Clean() { 132 for i := 0; i < HashMapSharding; i++ { 133 this.lockers[i].Lock() 134 } 135 136 // 这里不能简单清空 this.m ,避免导致别的数据无法写入 map 而产生 panic 137 for i := 0; i < HashMapSharding; i++ { 138 this.m[i] = map[uint64]zero.Zero{} 139 } 140 141 for i := HashMapSharding - 1; i >= 0; i-- { 142 this.lockers[i].Unlock() 143 } 144 } 145 146 func (this *SQLiteFileListHashMap) IsReady() bool { 147 return this.isReady 148 } 149 150 func (this *SQLiteFileListHashMap) Len() int { 151 for i := 0; i < HashMapSharding; i++ { 152 this.lockers[i].Lock() 153 } 154 155 var count = 0 156 for _, shard := range this.m { 157 count += len(shard) 158 } 159 160 for i := HashMapSharding - 1; i >= 0; i-- { 161 this.lockers[i].Unlock() 162 } 163 164 return count 165 } 166 167 func (this *SQLiteFileListHashMap) SetIsAvailable(isAvailable bool) { 168 this.isAvailable = isAvailable 169 } 170 171 func (this *SQLiteFileListHashMap) SetIsReady(isReady bool) { 172 this.isReady = isReady 173 } 174 175 func (this *SQLiteFileListHashMap) bigInt(hash string) (hashInt uint64, index int) { 176 var bigInt = bigIntPool.Get().(*big.Int) 177 bigInt.SetString(hash, 16) 178 hashInt = bigInt.Uint64() 179 bigIntPool.Put(bigInt) 180 181 index = int(hashInt % HashMapSharding) 182 return 183 }