Skip to content

Commit 66d785b

Browse files
Chris Elderdenyeart
authored andcommitted
[FAB-6176] Couchdb index management state database
Add couchdb index management capabilities to the state database layer. Add create and delete index functions to statecouchdb. Add HandleChaincodeDefinition to statecouchdb. Add unit tests for create and save index functions. Add unit tests for HandleChaincodeDefinition. Change-Id: I82f31ced8eb666e607c549f995d0c83fc7364664 Signed-off-by: Chris Elder <chris.elder@us.ibm.com>
1 parent 4fecdbd commit 66d785b

File tree

3 files changed

+263
-22
lines changed

3 files changed

+263
-22
lines changed

core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func TestBasicRW(t *testing.T, dbProvider statedb.VersionedDBProvider) {
9191
sp, err = db.GetLatestSavePoint()
9292
testutil.AssertNoError(t, err, "")
9393
testutil.AssertEquals(t, sp, savePoint)
94+
9495
}
9596

9697
// TestMultiDBBasicRW tests basic read-write on multiple dbs

core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@ SPDX-License-Identifier: Apache-2.0
66
package statecouchdb
77

88
import (
9+
"archive/tar"
910
"bytes"
1011
"encoding/json"
1112
"errors"
1213
"fmt"
14+
"io"
15+
"io/ioutil"
16+
"path/filepath"
1317
"strconv"
1418
"strings"
1519
"sync"
1620
"unicode/utf8"
1721

1822
"github.com/hyperledger/fabric/common/flogging"
23+
"github.com/hyperledger/fabric/core/ledger/cceventmgmt"
1924
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
2025
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
2126
"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
@@ -67,6 +72,68 @@ func NewVersionedDBProvider() (*VersionedDBProvider, error) {
6772
return &VersionedDBProvider{couchInstance, make(map[string]*VersionedDB), sync.Mutex{}, 0}, nil
6873
}
6974

75+
//HandleChaincodeDeploy initializes database artifacts for the database associated with the namespace
76+
func (vdb *VersionedDB) HandleChaincodeDeploy(chaincodeDefinition *cceventmgmt.ChaincodeDefinition, dbArtifactsTar []byte) error {
77+
78+
logger.Debugf("Entering HandleChaincodeDeploy")
79+
80+
if chaincodeDefinition == nil {
81+
return fmt.Errorf("chaincodeDefinition must not be nil")
82+
}
83+
84+
db, err := vdb.getNamespaceDBHandle(chaincodeDefinition.Name)
85+
if err != nil {
86+
return err
87+
}
88+
89+
//initialize a reader for the artifacts tar file
90+
artifactReader := bytes.NewReader(dbArtifactsTar)
91+
tarReader := tar.NewReader(artifactReader)
92+
93+
for {
94+
95+
//read the next header from the tar
96+
tarHeader, err := tarReader.Next()
97+
98+
//if the EOF is detected, then exit
99+
if err == io.EOF {
100+
// end of tar archive
101+
break
102+
}
103+
if err != nil {
104+
return err
105+
}
106+
107+
logger.Debugf("Reading artifact from file: %s", tarHeader.Name)
108+
109+
//Ensure that this is not a directory
110+
if !tarHeader.FileInfo().IsDir() {
111+
112+
//split the filename into directory and file name
113+
dir, file := filepath.Split(tarHeader.Name)
114+
115+
if dir == "META-INF/statedb/couchdb/indexes/" {
116+
117+
logger.Debugf("Creating index from file file: %s", file)
118+
119+
//read the tar entry into a byte array
120+
indexData, err := ioutil.ReadAll(tarReader)
121+
if err != nil {
122+
return err
123+
}
124+
125+
//create the index from the tar entry
126+
err = db.CreateIndex(string(indexData))
127+
if err != nil {
128+
return err
129+
}
130+
}
131+
}
132+
}
133+
134+
return nil
135+
}
136+
70137
// GetDBHandle gets the handle to a named database
71138
func (provider *VersionedDBProvider) GetDBHandle(dbName string) (statedb.VersionedDB, error) {
72139

core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb_test.go

Lines changed: 195 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ limitations under the License.
1717
package statecouchdb
1818

1919
import (
20+
"archive/tar"
21+
"bytes"
22+
"log"
2023
"os"
2124
"testing"
2225
"time"
2326

2427
"github.com/hyperledger/fabric/common/ledger/testutil"
28+
"github.com/hyperledger/fabric/core/ledger/cceventmgmt"
2529
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
2630
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/commontests"
2731
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
@@ -58,33 +62,51 @@ func TestMain(m *testing.M) {
5862

5963
func TestBasicRW(t *testing.T) {
6064
env := NewTestVDBEnv(t)
61-
env.Cleanup("testbasicrw")
62-
defer env.Cleanup("testbasicrw")
65+
env.Cleanup("testbasicrw_")
66+
env.Cleanup("testbasicrw_ns")
67+
env.Cleanup("testbasicrw_ns1")
68+
env.Cleanup("testbasicrw_ns2")
69+
defer env.Cleanup("testbasicrw_")
70+
defer env.Cleanup("testbasicrw_ns")
71+
defer env.Cleanup("testbasicrw_ns1")
72+
defer env.Cleanup("testbasicrw_ns2")
6373
commontests.TestBasicRW(t, env.DBProvider)
6474

6575
}
6676

6777
func TestMultiDBBasicRW(t *testing.T) {
6878
env := NewTestVDBEnv(t)
69-
env.Cleanup("testmultidbbasicrw")
70-
env.Cleanup("testmultidbbasicrw2")
71-
defer env.Cleanup("testmultidbbasicrw")
72-
defer env.Cleanup("testmultidbbasicrw2")
79+
env.Cleanup("testmultidbbasicrw_")
80+
env.Cleanup("testmultidbbasicrw_ns1")
81+
env.Cleanup("testmultidbbasicrw2_")
82+
env.Cleanup("testmultidbbasicrw2_ns1")
83+
defer env.Cleanup("testmultidbbasicrw_")
84+
defer env.Cleanup("testmultidbbasicrw_ns1")
85+
defer env.Cleanup("testmultidbbasicrw2_")
86+
defer env.Cleanup("testmultidbbasicrw2_ns1")
7387
commontests.TestMultiDBBasicRW(t, env.DBProvider)
7488

7589
}
7690

7791
func TestDeletes(t *testing.T) {
7892
env := NewTestVDBEnv(t)
79-
env.Cleanup("testdeletes")
80-
defer env.Cleanup("testdeletes")
93+
env.Cleanup("testdeletes_")
94+
env.Cleanup("testdeletes_ns")
95+
defer env.Cleanup("testdeletes_")
96+
defer env.Cleanup("testdeletes_ns")
8197
commontests.TestDeletes(t, env.DBProvider)
8298
}
8399

84100
func TestIterator(t *testing.T) {
85101
env := NewTestVDBEnv(t)
86-
env.Cleanup("testiterator")
87-
defer env.Cleanup("testiterator")
102+
env.Cleanup("testiterator_")
103+
env.Cleanup("testiterator_ns1")
104+
env.Cleanup("testiterator_ns2")
105+
env.Cleanup("testiterator_ns3")
106+
defer env.Cleanup("testiterator_")
107+
defer env.Cleanup("testiterator_ns1")
108+
defer env.Cleanup("testiterator_ns2")
109+
defer env.Cleanup("testiterator_ns3")
88110
commontests.TestIterator(t, env.DBProvider)
89111
}
90112

@@ -104,48 +126,68 @@ func testValueAndVersionEncoding(t *testing.T, value []byte, version *version.He
104126
// query test
105127
func TestQuery(t *testing.T) {
106128
env := NewTestVDBEnv(t)
107-
env.Cleanup("testquery")
108-
defer env.Cleanup("testquery")
129+
env.Cleanup("testquery_")
130+
env.Cleanup("testquery_ns1")
131+
env.Cleanup("testquery_ns2")
132+
env.Cleanup("testquery_ns3")
133+
defer env.Cleanup("testquery_")
134+
defer env.Cleanup("testquery_ns1")
135+
defer env.Cleanup("testquery_ns2")
136+
defer env.Cleanup("testquery_ns3")
109137
commontests.TestQuery(t, env.DBProvider)
110138
}
111139

112140
func TestGetStateMultipleKeys(t *testing.T) {
113141

114142
env := NewTestVDBEnv(t)
115-
env.Cleanup("testgetmultiplekeys")
116-
defer env.Cleanup("testgetmultiplekeys")
143+
env.Cleanup("testgetmultiplekeys_")
144+
env.Cleanup("testgetmultiplekeys_ns1")
145+
env.Cleanup("testgetmultiplekeys_ns2")
146+
defer env.Cleanup("testgetmultiplekeys_")
147+
defer env.Cleanup("testgetmultiplekeys_ns1")
148+
defer env.Cleanup("testgetmultiplekeys_ns2")
117149
commontests.TestGetStateMultipleKeys(t, env.DBProvider)
118150
}
119151

120152
func TestGetVersion(t *testing.T) {
121153
env := NewTestVDBEnv(t)
122-
env.Cleanup("testgetversion")
123-
defer env.Cleanup("testgetversion")
154+
env.Cleanup("testgetversion_")
155+
env.Cleanup("testgetversion_ns")
156+
env.Cleanup("testgetversion_ns2")
157+
defer env.Cleanup("testgetversion_")
158+
defer env.Cleanup("testgetversion_ns")
159+
defer env.Cleanup("testgetversion_ns2")
124160
commontests.TestGetVersion(t, env.DBProvider)
125161
}
126162

127163
func TestSmallBatchSize(t *testing.T) {
128164
viper.Set("ledger.state.couchDBConfig.maxBatchUpdateSize", 2)
129165
env := NewTestVDBEnv(t)
130-
env.Cleanup("testsmallbatchsize")
131-
defer env.Cleanup("testsmallbatchsize")
166+
env.Cleanup("testsmallbatchsize_")
167+
env.Cleanup("testsmallbatchsize_ns1")
168+
defer env.Cleanup("testsmallbatchsize_")
169+
defer env.Cleanup("testsmallbatchsize_ns1")
132170
defer viper.Set("ledger.state.couchDBConfig.maxBatchUpdateSize", 1000)
133171
commontests.TestSmallBatchSize(t, env.DBProvider)
134172
}
135173

136174
func TestBatchRetry(t *testing.T) {
137175
env := NewTestVDBEnv(t)
138-
env.Cleanup("testbatchretry")
139-
defer env.Cleanup("testbatchretry")
176+
env.Cleanup("testbatchretry_")
177+
env.Cleanup("testbatchretry_ns")
178+
env.Cleanup("testbatchretry_ns1")
179+
defer env.Cleanup("testbatchretry_")
180+
defer env.Cleanup("testbatchretry_ns")
181+
defer env.Cleanup("testbatchretry_ns1")
140182
commontests.TestBatchWithIndividualRetry(t, env.DBProvider)
141183
}
142184

143185
// TestUtilityFunctions tests utility functions
144186
func TestUtilityFunctions(t *testing.T) {
145187

146188
env := NewTestVDBEnv(t)
147-
env.Cleanup("testutilityfunctions")
148-
defer env.Cleanup("testutilityfunctions")
189+
env.Cleanup("testutilityfunctions_")
190+
defer env.Cleanup("testutilityfunctions_")
149191

150192
db, err := env.DBProvider.GetDBHandle("testutilityfunctions")
151193
testutil.AssertNoError(t, err, "")
@@ -177,3 +219,134 @@ func TestDebugFunctions(t *testing.T) {
177219
testutil.AssertEquals(t, printCompositeKeys(loadKeys), "[ns,key4],[ns,key4]")
178220

179221
}
222+
223+
func TestHandleChaincodeDeploy(t *testing.T) {
224+
225+
env := NewTestVDBEnv(t)
226+
env.Cleanup("testinit_")
227+
env.Cleanup("testinit_ns1")
228+
env.Cleanup("testinit_ns2")
229+
defer env.Cleanup("testinit_")
230+
defer env.Cleanup("testinit_ns1")
231+
defer env.Cleanup("testinit_ns2")
232+
233+
db, err := env.DBProvider.GetDBHandle("testinit")
234+
testutil.AssertNoError(t, err, "")
235+
db.Open()
236+
defer db.Close()
237+
batch := statedb.NewUpdateBatch()
238+
239+
jsonValue1 := "{\"asset_name\": \"marble1\",\"color\": \"blue\",\"size\": 1,\"owner\": \"tom\"}"
240+
batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1))
241+
jsonValue2 := "{\"asset_name\": \"marble2\",\"color\": \"blue\",\"size\": 2,\"owner\": \"jerry\"}"
242+
batch.Put("ns1", "key2", []byte(jsonValue2), version.NewHeight(1, 2))
243+
jsonValue3 := "{\"asset_name\": \"marble3\",\"color\": \"blue\",\"size\": 3,\"owner\": \"fred\"}"
244+
batch.Put("ns1", "key3", []byte(jsonValue3), version.NewHeight(1, 3))
245+
jsonValue4 := "{\"asset_name\": \"marble4\",\"color\": \"blue\",\"size\": 4,\"owner\": \"martha\"}"
246+
batch.Put("ns1", "key4", []byte(jsonValue4), version.NewHeight(1, 4))
247+
jsonValue5 := "{\"asset_name\": \"marble5\",\"color\": \"blue\",\"size\": 5,\"owner\": \"fred\"}"
248+
batch.Put("ns1", "key5", []byte(jsonValue5), version.NewHeight(1, 5))
249+
jsonValue6 := "{\"asset_name\": \"marble6\",\"color\": \"blue\",\"size\": 6,\"owner\": \"elaine\"}"
250+
batch.Put("ns1", "key6", []byte(jsonValue6), version.NewHeight(1, 6))
251+
jsonValue7 := "{\"asset_name\": \"marble7\",\"color\": \"blue\",\"size\": 7,\"owner\": \"fred\"}"
252+
batch.Put("ns1", "key7", []byte(jsonValue7), version.NewHeight(1, 7))
253+
jsonValue8 := "{\"asset_name\": \"marble8\",\"color\": \"blue\",\"size\": 8,\"owner\": \"elaine\"}"
254+
batch.Put("ns1", "key8", []byte(jsonValue8), version.NewHeight(1, 8))
255+
jsonValue9 := "{\"asset_name\": \"marble9\",\"color\": \"green\",\"size\": 9,\"owner\": \"fred\"}"
256+
batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9))
257+
jsonValue10 := "{\"asset_name\": \"marble10\",\"color\": \"green\",\"size\": 10,\"owner\": \"mary\"}"
258+
batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10))
259+
jsonValue11 := "{\"asset_name\": \"marble11\",\"color\": \"cyan\",\"size\": 1000007,\"owner\": \"joe\"}"
260+
batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11))
261+
262+
//add keys for a separate namespace
263+
batch.Put("ns2", "key1", []byte(jsonValue1), version.NewHeight(1, 12))
264+
batch.Put("ns2", "key2", []byte(jsonValue2), version.NewHeight(1, 13))
265+
batch.Put("ns2", "key3", []byte(jsonValue3), version.NewHeight(1, 14))
266+
batch.Put("ns2", "key4", []byte(jsonValue4), version.NewHeight(1, 15))
267+
batch.Put("ns2", "key5", []byte(jsonValue5), version.NewHeight(1, 16))
268+
batch.Put("ns2", "key6", []byte(jsonValue6), version.NewHeight(1, 17))
269+
batch.Put("ns2", "key7", []byte(jsonValue7), version.NewHeight(1, 18))
270+
batch.Put("ns2", "key8", []byte(jsonValue8), version.NewHeight(1, 19))
271+
batch.Put("ns2", "key9", []byte(jsonValue9), version.NewHeight(1, 20))
272+
batch.Put("ns2", "key10", []byte(jsonValue10), version.NewHeight(1, 21))
273+
274+
savePoint := version.NewHeight(2, 22)
275+
db.ApplyUpdates(batch, savePoint)
276+
277+
//Create a buffer for the tar file
278+
buffer := new(bytes.Buffer)
279+
tarWriter := tar.NewWriter(buffer)
280+
281+
//Add 2 index definitions
282+
var files = []struct {
283+
Name, Body string
284+
}{
285+
{"META-INF/statedb/couchdb/indexes/indexColorSortName.json", "{\"index\":{\"fields\":[{\"data.color\":\"desc\"}]},\"ddoc\":\"indexColorSortName\",\"name\":\"indexColorSortName\",\"type\":\"json\"}"},
286+
{"META-INF/statedb/couchdb/indexes/indexSizeSortName.json", "{\"index\":{\"fields\":[{\"data.size\":\"desc\"}]},\"ddoc\":\"indexSizeSortName\",\"name\":\"indexSizeSortName\",\"type\":\"json\"}"},
287+
}
288+
for _, file := range files {
289+
tarHeader := &tar.Header{
290+
Name: file.Name,
291+
Mode: 0600,
292+
Size: int64(len(file.Body)),
293+
}
294+
err := tarWriter.WriteHeader(tarHeader)
295+
testutil.AssertNoError(t, err, "")
296+
297+
_, err = tarWriter.Write([]byte(file.Body))
298+
testutil.AssertNoError(t, err, "")
299+
300+
}
301+
// Make sure to check the error on Close.
302+
if err := tarWriter.Close(); err != nil {
303+
log.Fatalln(err)
304+
}
305+
306+
//Create a query
307+
queryString := `{"selector":{"owner":"fred"}}`
308+
309+
_, err = db.ExecuteQuery("ns1", queryString)
310+
testutil.AssertNoError(t, err, "")
311+
312+
//Create a query with a sort
313+
queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}`
314+
315+
_, err = db.ExecuteQuery("ns1", queryString)
316+
testutil.AssertError(t, err, "Error should have been thrown for a missing index")
317+
318+
if handleDefinition, ok := db.(cceventmgmt.ChaincodeLifecycleEventListener); ok {
319+
320+
chaincodeDef := &cceventmgmt.ChaincodeDefinition{Name: "ns1", Hash: nil, Version: ""}
321+
322+
//Test HandleChaincodeDefinition with a valid tar file
323+
err := handleDefinition.HandleChaincodeDeploy(chaincodeDef, buffer.Bytes())
324+
testutil.AssertNoError(t, err, "")
325+
326+
//Test HandleChaincodeDefinition with a nil tar file
327+
err = handleDefinition.HandleChaincodeDeploy(chaincodeDef, nil)
328+
testutil.AssertNoError(t, err, "")
329+
330+
//Test HandleChaincodeDefinition with a bad tar file
331+
err = handleDefinition.HandleChaincodeDeploy(chaincodeDef, []byte(`This is a really bad tar file`))
332+
testutil.AssertError(t, err, "Error should have been thrown for a bad tar file")
333+
334+
//Test HandleChaincodeDefinition with a nil chaincodeDef
335+
err = handleDefinition.HandleChaincodeDeploy(nil, []byte(`This is a really bad tar file`))
336+
testutil.AssertError(t, err, "Error should have been thrown for a nil chaincodeDefinition")
337+
338+
//Sleep to allow time for index creation
339+
time.Sleep(100 * time.Millisecond)
340+
//Create a query with a sort
341+
queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}`
342+
343+
//Query should complete without error
344+
_, err = db.ExecuteQuery("ns1", queryString)
345+
testutil.AssertNoError(t, err, "")
346+
347+
//Query namespace "ns2", index is only created in "ns1". This should return an error.
348+
_, err = db.ExecuteQuery("ns2", queryString)
349+
testutil.AssertError(t, err, "Error should have been thrown for a missing index")
350+
351+
}
352+
}

0 commit comments

Comments
 (0)