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