github.com/TeaOSLab/EdgeNode@v1.3.8/internal/iplibrary/ip_list_kv.go (about) 1 // Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . 2 3 package iplibrary 4 5 import ( 6 "encoding/binary" 7 "errors" 8 "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" 9 "github.com/TeaOSLab/EdgeNode/internal/events" 10 "github.com/TeaOSLab/EdgeNode/internal/goman" 11 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 12 "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" 13 "github.com/TeaOSLab/EdgeNode/internal/utils/idles" 14 "github.com/TeaOSLab/EdgeNode/internal/utils/kvstore" 15 "testing" 16 "time" 17 ) 18 19 type KVIPList struct { 20 ipTable *kvstore.Table[*pb.IPItem] 21 versionsTable *kvstore.Table[int64] 22 23 encoder *IPItemEncoder[*pb.IPItem] 24 25 cleanTicker *time.Ticker 26 27 isClosed bool 28 29 offsetItemKey string 30 } 31 32 func NewKVIPList() (*KVIPList, error) { 33 var db = &KVIPList{ 34 cleanTicker: time.NewTicker(24 * time.Hour), 35 encoder: &IPItemEncoder[*pb.IPItem]{}, 36 } 37 err := db.init() 38 return db, err 39 } 40 41 func (this *KVIPList) init() error { 42 store, storeErr := kvstore.DefaultStore() 43 if storeErr != nil { 44 return storeErr 45 } 46 db, dbErr := store.NewDB("ip_list") 47 if dbErr != nil { 48 return dbErr 49 } 50 51 { 52 table, err := kvstore.NewTable[*pb.IPItem]("ip_items", this.encoder) 53 if err != nil { 54 return err 55 } 56 this.ipTable = table 57 58 err = table.AddFields("expiresAt") 59 if err != nil { 60 return err 61 } 62 63 db.AddTable(table) 64 } 65 66 { 67 table, err := kvstore.NewTable[int64]("versions", kvstore.NewIntValueEncoder[int64]()) 68 if err != nil { 69 return err 70 } 71 this.versionsTable = table 72 db.AddTable(table) 73 } 74 75 goman.New(func() { 76 events.OnClose(func() { 77 _ = this.Close() 78 this.cleanTicker.Stop() 79 }) 80 81 idles.RunTicker(this.cleanTicker, func() { 82 if this.isClosed { 83 return 84 } 85 deleteErr := this.DeleteExpiredItems() 86 if deleteErr != nil { 87 remotelogs.Error("IP_LIST_DB", "clean expired items failed: "+deleteErr.Error()) 88 } 89 }) 90 }) 91 92 return nil 93 } 94 95 // Name 数据库名称代号 96 func (this *KVIPList) Name() string { 97 return "kvstore" 98 } 99 100 // DeleteExpiredItems 删除过期的条目 101 func (this *KVIPList) DeleteExpiredItems() error { 102 if this.isClosed { 103 return nil 104 } 105 106 for { 107 var found bool 108 var currentTime = fasttime.Now().Unix() 109 err := this.ipTable. 110 Query(). 111 FieldAsc("expiresAt"). 112 ForUpdate(). 113 Limit(1000). 114 FindAll(func(tx *kvstore.Tx[*pb.IPItem], item kvstore.Item[*pb.IPItem]) (goNext bool, err error) { 115 if !item.Value.IsDeleted && item.Value.ExpiredAt == 0 { // never expires 116 return kvstore.Skip() 117 } 118 if item.Value.ExpiredAt < currentTime-7*86400 /** keep for 7 days **/ { 119 err = tx.Delete(item.Key) 120 if err != nil { 121 return false, err 122 } 123 found = true 124 return true, nil 125 } 126 127 found = false 128 return false, nil 129 }) 130 if err != nil { 131 return err 132 } 133 if !found { 134 break 135 } 136 } 137 138 return nil 139 } 140 141 func (this *KVIPList) AddItem(item *pb.IPItem) error { 142 if this.isClosed { 143 return nil 144 } 145 146 // 先删除 147 var key = this.encoder.EncodeKey(item) 148 err := this.ipTable.Delete(key) 149 if err != nil { 150 return err 151 } 152 153 // 如果是删除,则不再创建新记录 154 if item.IsDeleted { 155 return this.UpdateMaxVersion(item.Version) 156 } 157 158 err = this.ipTable.Set(key, item) 159 if err != nil { 160 return err 161 } 162 163 return this.UpdateMaxVersion(item.Version) 164 } 165 166 func (this *KVIPList) ReadItems(offset int64, size int64) (items []*pb.IPItem, goNextLoop bool, err error) { 167 if this.isClosed { 168 return 169 } 170 171 err = this.ipTable. 172 Query(). 173 Offset(this.offsetItemKey). 174 Limit(int(size)). 175 FindAll(func(tx *kvstore.Tx[*pb.IPItem], item kvstore.Item[*pb.IPItem]) (goNext bool, err error) { 176 this.offsetItemKey = item.Key 177 goNextLoop = true 178 179 if !item.Value.IsDeleted { 180 items = append(items, item.Value) 181 } 182 return true, nil 183 }) 184 return 185 } 186 187 // ReadMaxVersion 读取当前最大版本号 188 func (this *KVIPList) ReadMaxVersion() (int64, error) { 189 if this.isClosed { 190 return 0, errors.New("database has been closed") 191 } 192 193 version, err := this.versionsTable.Get("version") 194 if err != nil { 195 if kvstore.IsNotFound(err) { 196 return 0, nil 197 } 198 return 0, err 199 } 200 return version, nil 201 } 202 203 // UpdateMaxVersion 修改版本号 204 func (this *KVIPList) UpdateMaxVersion(version int64) error { 205 if this.isClosed { 206 return nil 207 } 208 209 return this.versionsTable.Set("version", version) 210 } 211 212 func (this *KVIPList) TestInspect(t *testing.T) error { 213 return this.ipTable. 214 Query(). 215 FindAll(func(tx *kvstore.Tx[*pb.IPItem], item kvstore.Item[*pb.IPItem]) (goNext bool, err error) { 216 if len(item.Key) != 8 { 217 return false, errors.New("invalid key '" + item.Key + "'") 218 } 219 220 t.Log(binary.BigEndian.Uint64([]byte(item.Key)), "=>", item.Value) 221 return true, nil 222 }) 223 } 224 225 // Flush to disk 226 func (this *KVIPList) Flush() error { 227 return this.ipTable.DB().Store().Flush() 228 } 229 230 func (this *KVIPList) Close() error { 231 this.isClosed = true 232 return nil 233 }