github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/shed/example_store_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:44</date> 10 //</624450117558079488> 11 12 13 package shed_test 14 15 import ( 16 "bytes" 17 "context" 18 "encoding/binary" 19 "fmt" 20 "io/ioutil" 21 "log" 22 "os" 23 "time" 24 25 "github.com/ethereum/go-ethereum/swarm/shed" 26 "github.com/ethereum/go-ethereum/swarm/storage" 27 "github.com/syndtr/goleveldb/leveldb" 28 ) 29 30 //存储保留字段和索引(包括其编码功能) 31 //并通过从中组合数据来定义对它们的操作。 32 //它实现了storage.chunkstore接口。 33 //这只是一个不支持并行操作的例子 34 //或者现实世界的实现。 35 type Store struct { 36 db *shed.DB 37 38 //字段和索引 39 schemaName shed.StringField 40 sizeCounter shed.Uint64Field 41 accessCounter shed.Uint64Field 42 retrievalIndex shed.Index 43 accessIndex shed.Index 44 gcIndex shed.Index 45 } 46 47 //新的返回新的存储。所有字段和索引都已初始化 48 //并检查与现有数据库中架构的可能冲突 49 //自动地。 50 func New(path string) (s *Store, err error) { 51 db, err := shed.NewDB(path, "") 52 if err != nil { 53 return nil, err 54 } 55 s = &Store{ 56 db: db, 57 } 58 //用任意名称标识当前存储架构。 59 s.schemaName, err = db.NewStringField("schema-name") 60 if err != nil { 61 return nil, err 62 } 63 //块访问的全局不断递增索引。 64 s.accessCounter, err = db.NewUint64Field("access-counter") 65 if err != nil { 66 return nil, err 67 } 68 //存储实际块地址、数据和存储时间戳的索引。 69 s.retrievalIndex, err = db.NewIndex("Address->StoreTimestamp|Data", shed.IndexFuncs{ 70 EncodeKey: func(fields shed.Item) (key []byte, err error) { 71 return fields.Address, nil 72 }, 73 DecodeKey: func(key []byte) (e shed.Item, err error) { 74 e.Address = key 75 return e, nil 76 }, 77 EncodeValue: func(fields shed.Item) (value []byte, err error) { 78 b := make([]byte, 8) 79 binary.BigEndian.PutUint64(b, uint64(fields.StoreTimestamp)) 80 value = append(b, fields.Data...) 81 return value, nil 82 }, 83 DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 84 e.StoreTimestamp = int64(binary.BigEndian.Uint64(value[:8])) 85 e.Data = value[8:] 86 return e, nil 87 }, 88 }) 89 if err != nil { 90 return nil, err 91 } 92 //存储特定地址的访问时间戳的索引。 93 //需要它来更新迭代顺序的GC索引键。 94 s.accessIndex, err = db.NewIndex("Address->AccessTimestamp", shed.IndexFuncs{ 95 EncodeKey: func(fields shed.Item) (key []byte, err error) { 96 return fields.Address, nil 97 }, 98 DecodeKey: func(key []byte) (e shed.Item, err error) { 99 e.Address = key 100 return e, nil 101 }, 102 EncodeValue: func(fields shed.Item) (value []byte, err error) { 103 b := make([]byte, 8) 104 binary.BigEndian.PutUint64(b, uint64(fields.AccessTimestamp)) 105 return b, nil 106 }, 107 DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 108 e.AccessTimestamp = int64(binary.BigEndian.Uint64(value)) 109 return e, nil 110 }, 111 }) 112 if err != nil { 113 return nil, err 114 } 115 //索引键按访问时间戳排序,用于垃圾收集优先级。 116 s.gcIndex, err = db.NewIndex("AccessTimestamp|StoredTimestamp|Address->nil", shed.IndexFuncs{ 117 EncodeKey: func(fields shed.Item) (key []byte, err error) { 118 b := make([]byte, 16, 16+len(fields.Address)) 119 binary.BigEndian.PutUint64(b[:8], uint64(fields.AccessTimestamp)) 120 binary.BigEndian.PutUint64(b[8:16], uint64(fields.StoreTimestamp)) 121 key = append(b, fields.Address...) 122 return key, nil 123 }, 124 DecodeKey: func(key []byte) (e shed.Item, err error) { 125 e.AccessTimestamp = int64(binary.BigEndian.Uint64(key[:8])) 126 e.StoreTimestamp = int64(binary.BigEndian.Uint64(key[8:16])) 127 e.Address = key[16:] 128 return e, nil 129 }, 130 EncodeValue: func(fields shed.Item) (value []byte, err error) { 131 return nil, nil 132 }, 133 DecodeValue: func(keyItem shed.Item, value []byte) (e shed.Item, err error) { 134 return e, nil 135 }, 136 }) 137 if err != nil { 138 return nil, err 139 } 140 return s, nil 141 } 142 143 //放置存储块并设置它存储时间戳。 144 func (s *Store) Put(_ context.Context, ch storage.Chunk) (err error) { 145 return s.retrievalIndex.Put(shed.Item{ 146 Address: ch.Address(), 147 Data: ch.Data(), 148 StoreTimestamp: time.Now().UTC().UnixNano(), 149 }) 150 } 151 152 //get使用提供的地址检索块。 153 //它通过删除以前的索引来更新访问和GC索引 154 //并添加新项作为索引项的键 155 //被改变了。 156 func (s *Store) Get(_ context.Context, addr storage.Address) (c storage.Chunk, err error) { 157 batch := new(leveldb.Batch) 158 159 //获取块数据和存储时间戳。 160 item, err := s.retrievalIndex.Get(shed.Item{ 161 Address: addr, 162 }) 163 if err != nil { 164 if err == leveldb.ErrNotFound { 165 return nil, storage.ErrChunkNotFound 166 } 167 return nil, err 168 } 169 170 //获取区块访问时间戳。 171 accessItem, err := s.accessIndex.Get(shed.Item{ 172 Address: addr, 173 }) 174 switch err { 175 case nil: 176 //如果找到访问时间戳,则删除gc索引项。 177 err = s.gcIndex.DeleteInBatch(batch, shed.Item{ 178 Address: item.Address, 179 StoreTimestamp: accessItem.AccessTimestamp, 180 AccessTimestamp: item.StoreTimestamp, 181 }) 182 if err != nil { 183 return nil, err 184 } 185 case leveldb.ErrNotFound: 186 //找不到访问时间戳。不要做任何事。 187 //这是firs get请求。 188 default: 189 return nil, err 190 } 191 192 //指定新的访问时间戳 193 accessTimestamp := time.Now().UTC().UnixNano() 194 195 //在访问索引中放置新的访问时间戳。 196 err = s.accessIndex.PutInBatch(batch, shed.Item{ 197 Address: addr, 198 AccessTimestamp: accessTimestamp, 199 }) 200 if err != nil { 201 return nil, err 202 } 203 204 //在gc索引中放置新的访问时间戳。 205 err = s.gcIndex.PutInBatch(batch, shed.Item{ 206 Address: item.Address, 207 AccessTimestamp: accessTimestamp, 208 StoreTimestamp: item.StoreTimestamp, 209 }) 210 if err != nil { 211 return nil, err 212 } 213 214 //递增访问计数器。 215 //目前此信息不在任何地方使用。 216 _, err = s.accessCounter.IncInBatch(batch) 217 if err != nil { 218 return nil, err 219 } 220 221 //编写批处理。 222 err = s.db.WriteBatch(batch) 223 if err != nil { 224 return nil, err 225 } 226 227 //返回块。 228 return storage.NewChunk(item.Address, item.Data), nil 229 } 230 231 //Collect垃圾是索引迭代的一个例子。 232 //它不提供可靠的垃圾收集功能。 233 func (s *Store) CollectGarbage() (err error) { 234 const maxTrashSize = 100 235 maxRounds := 10 //任意数,需要计算 236 237 //进行几轮GC测试。 238 for roundCount := 0; roundCount < maxRounds; roundCount++ { 239 var garbageCount int 240 //新一轮CG的新批次。 241 trash := new(leveldb.Batch) 242 //遍历所有索引项,并在需要时中断。 243 err = s.gcIndex.Iterate(func(item shed.Item) (stop bool, err error) { 244 //移除区块。 245 err = s.retrievalIndex.DeleteInBatch(trash, item) 246 if err != nil { 247 return false, err 248 } 249 //删除gc索引中的元素。 250 err = s.gcIndex.DeleteInBatch(trash, item) 251 if err != nil { 252 return false, err 253 } 254 //删除访问索引中的关系。 255 err = s.accessIndex.DeleteInBatch(trash, item) 256 if err != nil { 257 return false, err 258 } 259 garbageCount++ 260 if garbageCount >= maxTrashSize { 261 return true, nil 262 } 263 return false, nil 264 }, nil) 265 if err != nil { 266 return err 267 } 268 if garbageCount == 0 { 269 return nil 270 } 271 err = s.db.WriteBatch(trash) 272 if err != nil { 273 return err 274 } 275 } 276 return nil 277 } 278 279 //getSchema是检索最简单的 280 //数据库字段中的字符串。 281 func (s *Store) GetSchema() (name string, err error) { 282 name, err = s.schemaName.Get() 283 if err == leveldb.ErrNotFound { 284 return "", nil 285 } 286 return name, err 287 } 288 289 //getSchema是存储最简单的 290 //数据库字段中的字符串。 291 func (s *Store) PutSchema(name string) (err error) { 292 return s.schemaName.Put(name) 293 } 294 295 //关闭关闭基础数据库。 296 func (s *Store) Close() error { 297 return s.db.Close() 298 } 299 300 //示例存储使用shed包构造一个简单的存储实现。 301 func Example_store() { 302 dir, err := ioutil.TempDir("", "ephemeral") 303 if err != nil { 304 log.Fatal(err) 305 } 306 defer os.RemoveAll(dir) 307 308 s, err := New(dir) 309 if err != nil { 310 log.Fatal(err) 311 } 312 defer s.Close() 313 314 ch := storage.GenerateRandomChunk(1024) 315 err = s.Put(context.Background(), ch) 316 if err != nil { 317 log.Fatal(err) 318 } 319 320 got, err := s.Get(context.Background(), ch.Address()) 321 if err != nil { 322 log.Fatal(err) 323 } 324 325 fmt.Println(bytes.Equal(got.Data(), ch.Data())) 326 327 //输出:真 328 } 329