github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/storage/localstore/mode_get.go (about) 1 // Copyleft 2018 The susy-graviton Authors 2 // This file is part of the susy-graviton library. 3 // 4 // The susy-graviton library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The susy-graviton library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MSRCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the susy-graviton library. If not, see <http://www.gnu.org/licenses/>. 16 17 package localstore 18 19 import ( 20 "github.com/susy-go/susy-graviton/log" 21 "github.com/susy-go/susy-graviton/swarm/shed" 22 "github.com/susy-go/susy-graviton/swarm/storage" 23 "github.com/syndtr/goleveldb/leveldb" 24 ) 25 26 // ModeGet enumerates different Getter modes. 27 type ModeGet int 28 29 // Getter modes. 30 const ( 31 // ModeGetRequest: when accessed for retrieval 32 ModeGetRequest ModeGet = iota 33 // ModeGetSync: when accessed for syncing or proof of custody request 34 ModeGetSync 35 ) 36 37 // Getter provides Get method to retrieve Chunks 38 // from database. 39 type Getter struct { 40 db *DB 41 mode ModeGet 42 } 43 44 // NewGetter returns a new Getter on database 45 // with a specific Mode. 46 func (db *DB) NewGetter(mode ModeGet) *Getter { 47 return &Getter{ 48 mode: mode, 49 db: db, 50 } 51 } 52 53 // Get returns a chunk from the database. If the chunk is 54 // not found storage.ErrChunkNotFound will be returned. 55 // All required indexes will be updated required by the 56 // Getter Mode. 57 func (g *Getter) Get(addr storage.Address) (chunk storage.Chunk, err error) { 58 out, err := g.db.get(g.mode, addr) 59 if err != nil { 60 if err == leveldb.ErrNotFound { 61 return nil, storage.ErrChunkNotFound 62 } 63 return nil, err 64 } 65 return storage.NewChunk(out.Address, out.Data), nil 66 } 67 68 // get returns Item from the retrieval index 69 // and updates other indexes. 70 func (db *DB) get(mode ModeGet, addr storage.Address) (out shed.Item, err error) { 71 item := addressToItem(addr) 72 73 out, err = db.retrievalDataIndex.Get(item) 74 if err != nil { 75 return out, err 76 } 77 switch mode { 78 // update the access timestamp and gc index 79 case ModeGetRequest: 80 if db.updateGCSem != nil { 81 // wait before creating new goroutines 82 // if updateGCSem buffer id full 83 db.updateGCSem <- struct{}{} 84 } 85 db.updateGCWG.Add(1) 86 go func() { 87 defer db.updateGCWG.Done() 88 if db.updateGCSem != nil { 89 // free a spot in updateGCSem buffer 90 // for a new goroutine 91 defer func() { <-db.updateGCSem }() 92 } 93 err := db.updateGC(out) 94 if err != nil { 95 log.Error("localstore update gc", "err", err) 96 } 97 // if gc update hook is defined, call it 98 if testHookUpdateGC != nil { 99 testHookUpdateGC() 100 } 101 }() 102 103 // no updates to indexes 104 case ModeGetSync: 105 default: 106 return out, ErrInvalidMode 107 } 108 return out, nil 109 } 110 111 // updateGC updates garbage collection index for 112 // a single item. Provided item is expected to have 113 // only Address and Data fields with non zero values, 114 // which is ensured by the get function. 115 func (db *DB) updateGC(item shed.Item) (err error) { 116 unlock, err := db.lockAddr(item.Address) 117 if err != nil { 118 return err 119 } 120 defer unlock() 121 122 batch := new(leveldb.Batch) 123 124 // update accessTimeStamp in retrieve, gc 125 126 i, err := db.retrievalAccessIndex.Get(item) 127 switch err { 128 case nil: 129 item.AccessTimestamp = i.AccessTimestamp 130 case leveldb.ErrNotFound: 131 // no chunk accesses 132 default: 133 return err 134 } 135 if item.AccessTimestamp == 0 { 136 // chunk is not yet synced 137 // do not add it to the gc index 138 return nil 139 } 140 // delete current entry from the gc index 141 db.gcIndex.DeleteInBatch(batch, item) 142 // update access timestamp 143 item.AccessTimestamp = now() 144 // update retrieve access index 145 db.retrievalAccessIndex.PutInBatch(batch, item) 146 // add new entry to gc index 147 db.gcIndex.PutInBatch(batch, item) 148 149 return db.shed.WriteBatch(batch) 150 } 151 152 // testHookUpdateGC is a hook that can provide 153 // information when a garbage collection index is updated. 154 var testHookUpdateGC func()