@@ -17,7 +17,9 @@ limitations under the License.
17
17
package kvledger
18
18
19
19
import (
20
+ "bytes"
20
21
"errors"
22
+ "fmt"
21
23
22
24
"github.com/hyperledger/fabric/common/ledger/blkstorage"
23
25
"github.com/hyperledger/fabric/common/ledger/blkstorage/fsblkstorage"
@@ -29,6 +31,9 @@ import (
29
31
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb"
30
32
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb"
31
33
"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
34
+ "github.com/hyperledger/fabric/protos/common"
35
+ "github.com/hyperledger/fabric/protos/utils"
36
+ "github.com/syndtr/goleveldb/leveldb"
32
37
)
33
38
34
39
var (
38
43
ErrNonExistingLedgerID = errors .New ("LedgerID does not exist" )
39
44
// ErrLedgerNotOpened is thrown by a CloseLedger call if a ledger with the given id has not been opened
40
45
ErrLedgerNotOpened = errors .New ("Ledger is not opened yet" )
46
+
47
+ underConstructionLedgerKey = []byte ("underConstructionLedgerKey" )
48
+ ledgerKeyPrefix = []byte ("l" )
41
49
)
42
50
43
51
// Provider implements interface ledger.PeerLedgerProvider
@@ -90,7 +98,44 @@ func NewProvider() (ledger.PeerLedgerProvider, error) {
90
98
historydbProvider = historyleveldb .NewHistoryDBProvider ()
91
99
92
100
logger .Info ("ledger provider Initialized" )
93
- return & Provider {idStore , blockStoreProvider , vdbProvider , historydbProvider }, nil
101
+ provider := & Provider {idStore , blockStoreProvider , vdbProvider , historydbProvider }
102
+ provider .recoverUnderConstructionLedger ()
103
+ return provider , nil
104
+ }
105
+
106
+ // CreateWithGenesisBlock implements the corresponding method from interface ledger.PeerLedgerProvider
107
+ // This functions sets a under construction flag before doing any thing related to ledger creation and
108
+ // upon a successful ledger creation with the committed genesis block, removes the flag and add entry into
109
+ // created ledgers list (atomically). If a crash happens in between, the 'recoverUnderConstructionLedger'
110
+ // function is invoked before declaring the provider to be usable
111
+ func (provider * Provider ) CreateWithGenesisBlock (genesisBlock * common.Block ) (ledger.PeerLedger , error ) {
112
+ ledgerID , err := utils .GetChainIDFromBlock (genesisBlock )
113
+ if err != nil {
114
+ return nil , err
115
+ }
116
+ exists , err := provider .idStore .ledgerIDExists (ledgerID )
117
+ if err != nil {
118
+ return nil , err
119
+ }
120
+ if exists {
121
+ return nil , ErrLedgerIDExists
122
+ }
123
+ if err = provider .idStore .setUnderConstructionFlag (ledgerID ); err != nil {
124
+ return nil , err
125
+ }
126
+ ledger , err := provider .openInternal (ledgerID )
127
+ if err != nil {
128
+ logger .Errorf ("Error in opening a new empty ledger. Unsetting under construction flag. Err: %s" , err )
129
+ panicOnErr (provider .runCleanup (ledgerID ), "Error while running cleanup for ledger id [%s]" , ledgerID )
130
+ panicOnErr (provider .idStore .unsetUnderConstructionFlag (), "Error while unsetting under construction flag" )
131
+ return nil , err
132
+ }
133
+ if err := ledger .Commit (genesisBlock ); err != nil {
134
+ ledger .Close ()
135
+ return nil , err
136
+ }
137
+ panicOnErr (provider .idStore .createLedgerID (ledgerID ), "Error while marking ledger as created" )
138
+ return ledger , nil
94
139
}
95
140
96
141
// Create implements the corresponding method from interface ledger.PeerLedgerProvider
@@ -102,15 +147,20 @@ func (provider *Provider) Create(ledgerID string) (ledger.PeerLedger, error) {
102
147
if exists {
103
148
return nil , ErrLedgerIDExists
104
149
}
105
- provider .idStore .createLedgerID (ledgerID )
106
- return provider .Open (ledgerID )
150
+ ledger , err := provider .openInternal (ledgerID )
151
+ if err != nil {
152
+ return nil , err
153
+ }
154
+ if err = provider .idStore .createLedgerID (ledgerID ); err != nil {
155
+ ledger .Close ()
156
+ return nil , err
157
+ }
158
+ return ledger , nil
107
159
}
108
160
109
161
// Open implements the corresponding method from interface ledger.PeerLedgerProvider
110
162
func (provider * Provider ) Open (ledgerID string ) (ledger.PeerLedger , error ) {
111
-
112
163
logger .Debugf ("Open() opening kvledger: %s" , ledgerID )
113
-
114
164
// Check the ID store to ensure that the chainId/ledgerId exists
115
165
exists , err := provider .idStore .ledgerIDExists (ledgerID )
116
166
if err != nil {
@@ -119,7 +169,10 @@ func (provider *Provider) Open(ledgerID string) (ledger.PeerLedger, error) {
119
169
if ! exists {
120
170
return nil , ErrNonExistingLedgerID
121
171
}
172
+ return provider .openInternal (ledgerID )
173
+ }
122
174
175
+ func (provider * Provider ) openInternal (ledgerID string ) (ledger.PeerLedger , error ) {
123
176
// Get the block store for a chain/ledger
124
177
blockStore , err := provider .blockStoreProvider .OpenBlockStore (ledgerID )
125
178
if err != nil {
@@ -165,6 +218,63 @@ func (provider *Provider) Close() {
165
218
provider .historydbProvider .Close ()
166
219
}
167
220
221
+ // recoverUnderConstructionLedger checks whether the under construction flag is set - this would be the case
222
+ // if a crash had happened during creation of ledger and the ledger creation could have been left in intermediate
223
+ // state. Recovery checks if the ledger was created and the genesis block was committed successfully then it completes
224
+ // the last step of adding the ledger id to the list of created ledgers. Else, it clears the under construction flag
225
+ func (provider * Provider ) recoverUnderConstructionLedger () {
226
+ logger .Debugf ("Recovering under construction ledger" )
227
+ ledgerID , err := provider .idStore .getUnderConstructionFlag ()
228
+ panicOnErr (err , "Error while checking whether the under construction flag is set" )
229
+ if ledgerID == "" {
230
+ logger .Debugf ("No under construction ledger found. Quitting recovery" )
231
+ return
232
+ }
233
+ logger .Infof ("ledger [%s] found as under construction" , ledgerID )
234
+ ledger , err := provider .openInternal (ledgerID )
235
+ panicOnErr (err , "Error while opening under construction ledger [%s]" , ledgerID )
236
+ bcInfo , err := ledger .GetBlockchainInfo ()
237
+ panicOnErr (err , "Error while getting blockchain info for the under construction ledger [%s]" , ledgerID )
238
+ ledger .Close ()
239
+
240
+ switch bcInfo .Height {
241
+ case 0 :
242
+ logger .Infof ("Genesis block was not committed. Hence, the peer ledger not created. unsetting the under construction flag" )
243
+ panicOnErr (provider .runCleanup (ledgerID ), "Error while running cleanup for ledger id [%s]" , ledgerID )
244
+ panicOnErr (provider .idStore .unsetUnderConstructionFlag (), "Error while unsetting under construction flag" )
245
+ case 1 :
246
+ logger .Infof ("Genesis block was committed. Hence, marking the peer ledger as created" )
247
+ panicOnErr (provider .idStore .createLedgerID (ledgerID ), "Error while adding ledgerID [%s] to created list" , ledgerID )
248
+ default :
249
+ panic (fmt .Errorf (
250
+ "Data inconsistency: under construction flag is set for ledger [%s] while the height of the blockchain is [%d]" ,
251
+ ledgerID , bcInfo .Height ))
252
+ }
253
+ return
254
+ }
255
+
256
+ // runCleanup cleans up blockstorage, statedb, and historydb for what
257
+ // may have got created during in-complete ledger creation
258
+ func (provider * Provider ) runCleanup (ledgerID string ) error {
259
+ // TODO - though, not having this is harmless for kv ledger.
260
+ // If we want, following could be done:
261
+ // - blockstorage could remove empty folders
262
+ // - couchdb backed statedb could delete the database if got created
263
+ // - leveldb backed statedb and history db need not perform anything as it uses a single db shared across ledgers
264
+ return nil
265
+ }
266
+
267
+ func panicOnErr (err error , mgsFormat string , args ... interface {}) {
268
+ if err == nil {
269
+ return
270
+ }
271
+ args = append (args , err )
272
+ panic (fmt .Sprintf (mgsFormat + " Err:%s " , args ... ))
273
+ }
274
+
275
+ //////////////////////////////////////////////////////////////////////
276
+ // Ledger id persistence related code
277
+ ///////////////////////////////////////////////////////////////////////
168
278
type idStore struct {
169
279
db * leveldbhelper.DB
170
280
}
@@ -175,8 +285,24 @@ func openIDStore(path string) *idStore {
175
285
return & idStore {db }
176
286
}
177
287
288
+ func (s * idStore ) setUnderConstructionFlag (ledgerID string ) error {
289
+ return s .db .Put (underConstructionLedgerKey , []byte (ledgerID ), true )
290
+ }
291
+
292
+ func (s * idStore ) unsetUnderConstructionFlag () error {
293
+ return s .db .Delete (underConstructionLedgerKey , true )
294
+ }
295
+
296
+ func (s * idStore ) getUnderConstructionFlag () (string , error ) {
297
+ val , err := s .db .Get (underConstructionLedgerKey )
298
+ if err != nil {
299
+ return "" , err
300
+ }
301
+ return string (val ), nil
302
+ }
303
+
178
304
func (s * idStore ) createLedgerID (ledgerID string ) error {
179
- key := [] byte (ledgerID )
305
+ key := s . encodeLedgerKey (ledgerID )
180
306
val := []byte {}
181
307
err := error (nil )
182
308
if val , err = s .db .Get (key ); err != nil {
@@ -185,11 +311,14 @@ func (s *idStore) createLedgerID(ledgerID string) error {
185
311
if val != nil {
186
312
return ErrLedgerIDExists
187
313
}
188
- return s .db .Put (key , val , true )
314
+ batch := & leveldb.Batch {}
315
+ batch .Put (key , val )
316
+ batch .Delete (underConstructionLedgerKey )
317
+ return s .db .WriteBatch (batch , true )
189
318
}
190
319
191
320
func (s * idStore ) ledgerIDExists (ledgerID string ) (bool , error ) {
192
- key := [] byte (ledgerID )
321
+ key := s . encodeLedgerKey (ledgerID )
193
322
val := []byte {}
194
323
err := error (nil )
195
324
if val , err = s .db .Get (key ); err != nil {
@@ -203,8 +332,11 @@ func (s *idStore) getAllLedgerIds() ([]string, error) {
203
332
itr := s .db .GetIterator (nil , nil )
204
333
itr .First ()
205
334
for itr .Valid () {
206
- key := string (itr .Key ())
207
- ids = append (ids , key )
335
+ if bytes .Equal (itr .Key (), underConstructionLedgerKey ) {
336
+ continue
337
+ }
338
+ id := string (s .decodeLedgerID (itr .Key ()))
339
+ ids = append (ids , id )
208
340
itr .Next ()
209
341
}
210
342
return ids , nil
@@ -213,3 +345,11 @@ func (s *idStore) getAllLedgerIds() ([]string, error) {
213
345
func (s * idStore ) close () {
214
346
s .db .Close ()
215
347
}
348
+
349
+ func (s * idStore ) encodeLedgerKey (ledgerID string ) []byte {
350
+ return append (ledgerKeyPrefix , []byte (ledgerID )... )
351
+ }
352
+
353
+ func (s * idStore ) decodeLedgerID (key []byte ) string {
354
+ return string (key [len (ledgerKeyPrefix ):])
355
+ }
0 commit comments