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  }