Skip to content

Commit 2ebd342

Browse files
author
Chris Elder
committed
FAB-1172 - Advanced simulation functions for CouchDB
Added implementations for the following: QueryExecutor.GetStateRangeScanIterator() QueryExecutor.GetStateMultipleKeys() TxSimulator.DeleteState() TxSimulator.SetStateMultipleKeys() Unit testing was extended for the range query in couchdb.go Additional unit testing for simulation functions will be added following a refactoring of unit tests for goleveldb. Change-Id: Id0907ace75767fe4b296f16c10d18693718d790f Signed-off-by: Chris Elder <[email protected]>
1 parent 87a0ce8 commit 2ebd342

File tree

5 files changed

+464
-39
lines changed

5 files changed

+464
-39
lines changed

core/ledger/kvledger/txmgmt/couchdbtxmgmt/couchdb_query_executer.go

+39-2
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,27 @@ func (q *CouchDBQueryExecutor) GetState(ns string, key string) ([]byte, error) {
3939

4040
// GetStateMultipleKeys implements method in interface `ledger.QueryExecutor`
4141
func (q *CouchDBQueryExecutor) GetStateMultipleKeys(namespace string, keys []string) ([][]byte, error) {
42-
return nil, errors.New("Not yet implemented")
42+
var results [][]byte
43+
var value []byte
44+
var err error
45+
for _, key := range keys {
46+
value, err = q.GetState(namespace, key)
47+
if err != nil {
48+
return nil, err
49+
}
50+
results = append(results, value)
51+
}
52+
return results, nil
4353
}
4454

4555
// GetStateRangeScanIterator implements method in interface `ledger.QueryExecutor`
4656
func (q *CouchDBQueryExecutor) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ledger.ResultsIterator, error) {
47-
return nil, errors.New("Not yet implemented")
57+
//q.checkDone()
58+
scanner, err := q.txmgr.getCommittedRangeScanner(namespace, startKey, endKey)
59+
if err != nil {
60+
return nil, err
61+
}
62+
return &qKVItr{scanner}, nil
4863
}
4964

5065
// GetTransactionsForKey - implements method in interface `ledger.QueryExecutor`
@@ -61,3 +76,25 @@ func (q *CouchDBQueryExecutor) ExecuteQuery(query string) (ledger.ResultsIterato
6176
func (q *CouchDBQueryExecutor) Done() {
6277
//TODO - acquire lock when constructing and release the lock here
6378
}
79+
80+
type qKVItr struct {
81+
s *kvScanner
82+
}
83+
84+
// Next implements Next() method in ledger.ResultsIterator
85+
func (itr *qKVItr) Next() (ledger.QueryResult, error) {
86+
committedKV, err := itr.s.next()
87+
if err != nil {
88+
return nil, err
89+
}
90+
if committedKV == nil {
91+
return nil, nil
92+
}
93+
94+
return &ledger.KV{Key: committedKV.key, Value: committedKV.value}, nil
95+
}
96+
97+
// Close implements Close() method in ledger.ResultsIterator
98+
func (itr *qKVItr) Close() {
99+
itr.s.close()
100+
}

core/ledger/kvledger/txmgmt/couchdbtxmgmt/couchdb_tx_simulator.go

+48-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"reflect"
2222

23+
"github.com/hyperledger/fabric/core/ledger"
2324
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt"
2425
logging "github.com/op/go-logging"
2526
)
@@ -108,6 +109,16 @@ func (s *CouchDBTxSimulator) GetState(ns string, key string) ([]byte, error) {
108109
return value, nil
109110
}
110111

112+
// GetStateRangeScanIterator implements method in interface `ledger.QueryExecutor`
113+
func (s *CouchDBTxSimulator) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ledger.ResultsIterator, error) {
114+
//s.checkDone()
115+
scanner, err := s.txmgr.getCommittedRangeScanner(namespace, startKey, endKey)
116+
if err != nil {
117+
return nil, err
118+
}
119+
return &sKVItr{scanner, s}, nil
120+
}
121+
111122
// SetState implements method in interface `ledger.TxSimulator`
112123
func (s *CouchDBTxSimulator) SetState(ns string, key string, value []byte) error {
113124
logger.Debugf("===COUCHDB=== Entering CouchDBTxSimulator.SetState()")
@@ -190,7 +201,12 @@ func (s *CouchDBTxSimulator) GetTxSimulationResults() ([]byte, error) {
190201

191202
// SetStateMultipleKeys implements method in interface `ledger.TxSimulator`
192203
func (s *CouchDBTxSimulator) SetStateMultipleKeys(namespace string, kvs map[string][]byte) error {
193-
return errors.New("Not yet implemented")
204+
for k, v := range kvs {
205+
if err := s.SetState(namespace, k, v); err != nil {
206+
return err
207+
}
208+
}
209+
return nil
194210
}
195211

196212
// CopyState implements method in interface `ledger.TxSimulator`
@@ -202,3 +218,34 @@ func (s *CouchDBTxSimulator) CopyState(sourceNamespace string, targetNamespace s
202218
func (s *CouchDBTxSimulator) ExecuteUpdate(query string) error {
203219
return errors.New("Not supported by KV data model")
204220
}
221+
222+
type sKVItr struct {
223+
scanner *kvScanner
224+
simulator *CouchDBTxSimulator
225+
}
226+
227+
// Next implements Next() method in ledger.ResultsIterator
228+
// Returns the next item in the result set. The `QueryResult` is expected to be nil when
229+
// the iterator gets exhausted
230+
func (itr *sKVItr) Next() (ledger.QueryResult, error) {
231+
committedKV, err := itr.scanner.next()
232+
if err != nil {
233+
return nil, err
234+
}
235+
if committedKV == nil {
236+
return nil, nil
237+
}
238+
239+
// Get existing cache for RW at the namespace of the result set if it exists. If none exists, then create it.
240+
nsRWs := itr.simulator.getOrCreateNsRWHolder(itr.scanner.namespace)
241+
nsRWs.readMap[committedKV.key] = &kvReadCache{
242+
&txmgmt.KVRead{Key: committedKV.key, Version: committedKV.version}, committedKV.value}
243+
244+
return &ledger.KV{Key: committedKV.key, Value: committedKV.value}, nil
245+
}
246+
247+
// Close implements Close() method in ledger.ResultsIterator
248+
// which releases resources occupied by the iterator.
249+
func (itr *sKVItr) Close() {
250+
itr.scanner.close()
251+
}

core/ledger/kvledger/txmgmt/couchdbtxmgmt/couchdb_txmgr.go

+63-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package couchdbtxmgmt
1818

1919
import (
20+
"bytes"
2021
"encoding/json"
2122
"errors"
2223
"sync"
@@ -36,6 +37,8 @@ import (
3637

3738
var logger = logging.MustGetLogger("couchdbtxmgmt")
3839

40+
var compositeKeySep = []byte{0x00}
41+
3942
// Conf - configuration for `CouchDBTxMgr`
4043
type Conf struct {
4144
DBPath string
@@ -383,6 +386,22 @@ func (txmgr *CouchDBTxMgr) getCommittedValueAndVersion(ns string, key string) ([
383386
return docBytes, ver, nil
384387
}
385388

389+
//getCommittedRangeScanner contructs composite start and end keys based on the namespace then calls the CouchDB range scanner
390+
func (txmgr *CouchDBTxMgr) getCommittedRangeScanner(namespace string, startKey string, endKey string) (*kvScanner, error) {
391+
var compositeStartKey []byte
392+
var compositeEndKey []byte
393+
if startKey != "" {
394+
compositeStartKey = constructCompositeKey(namespace, startKey)
395+
}
396+
if endKey != "" {
397+
compositeEndKey = constructCompositeKey(namespace, endKey)
398+
}
399+
400+
queryResult, _ := txmgr.couchDB.ReadDocRange(string(compositeStartKey), string(compositeEndKey), 1000, 0)
401+
402+
return newKVScanner(namespace, *queryResult), nil
403+
}
404+
386405
func encodeValue(value []byte, version uint64) []byte {
387406
versionBytes := proto.EncodeVarint(version)
388407
deleteMarker := 0
@@ -409,7 +428,50 @@ func decodeValue(encodedValue []byte) ([]byte, uint64) {
409428

410429
func constructCompositeKey(ns string, key string) []byte {
411430
compositeKey := []byte(ns)
412-
compositeKey = append(compositeKey, byte(0))
431+
compositeKey = append(compositeKey, compositeKeySep...)
413432
compositeKey = append(compositeKey, []byte(key)...)
414433
return compositeKey
415434
}
435+
436+
func splitCompositeKey(compositeKey []byte) (string, string) {
437+
split := bytes.SplitN(compositeKey, compositeKeySep, 2)
438+
return string(split[0]), string(split[1])
439+
}
440+
441+
type kvScanner struct {
442+
cursor int
443+
namespace string
444+
results []couchdb.QueryResult
445+
}
446+
447+
type committedKV struct {
448+
key string
449+
version *version.Height
450+
value []byte
451+
}
452+
453+
func newKVScanner(namespace string, queryResults []couchdb.QueryResult) *kvScanner {
454+
return &kvScanner{-1, namespace, queryResults}
455+
}
456+
457+
func (scanner *kvScanner) next() (*committedKV, error) {
458+
459+
scanner.cursor++
460+
461+
if scanner.cursor >= len(scanner.results) {
462+
return nil, nil
463+
}
464+
465+
selectedValue := scanner.results[scanner.cursor]
466+
467+
_, key := splitCompositeKey([]byte(selectedValue.ID))
468+
469+
//TODO - change hardcoded version when version support is available in CouchDB
470+
return &committedKV{key, version.NewHeight(1, 1), selectedValue.Value}, nil
471+
472+
}
473+
474+
func (scanner *kvScanner) close() {
475+
476+
scanner = nil
477+
}

0 commit comments

Comments
 (0)