github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb_test.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package statecouchdb 8 9 import ( 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "strings" 15 "testing" 16 "time" 17 "unicode/utf8" 18 19 "github.com/hechain20/hechain/common/flogging" 20 "github.com/hechain20/hechain/common/ledger/dataformat" 21 "github.com/hechain20/hechain/common/metrics/disabled" 22 "github.com/hechain20/hechain/core/ledger" 23 "github.com/hechain20/hechain/core/ledger/internal/version" 24 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb" 25 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb/commontests" 26 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb/mock" 27 "github.com/stretchr/testify/require" 28 ) 29 30 // testVDBEnv provides a couch db backed versioned db for testing 31 type testVDBEnv struct { 32 t *testing.T 33 DBProvider statedb.VersionedDBProvider 34 config *ledger.CouchDBConfig 35 cache *cache 36 sysNamespaces []string 37 couchDBEnv *testCouchDBEnv 38 } 39 40 func (env *testVDBEnv) init(t *testing.T, sysNamespaces []string) { 41 t.Logf("Initializing TestVDBEnv") 42 43 if env.couchDBEnv == nil { 44 couchDBEnv := &testCouchDBEnv{} 45 couchDBEnv.startCouchDB(t) 46 env.couchDBEnv = couchDBEnv 47 } 48 49 redoPath, err := ioutil.TempDir("", "cvdbenv") 50 if err != nil { 51 t.Fatalf("Failed to create redo log directory: %s", err) 52 } 53 config := &ledger.CouchDBConfig{ 54 Address: env.couchDBEnv.couchAddress, 55 Username: "admin", 56 Password: "adminpw", 57 InternalQueryLimit: 1000, 58 MaxBatchUpdateSize: 1000, 59 MaxRetries: 3, 60 MaxRetriesOnStartup: 20, 61 RequestTimeout: 35 * time.Second, 62 RedoLogPath: redoPath, 63 UserCacheSizeMBs: 8, 64 } 65 66 disableKeepAlive = true 67 dbProvider, err := NewVersionedDBProvider(config, &disabled.Provider{}, sysNamespaces) 68 if err != nil { 69 t.Fatalf("Error creating CouchDB Provider: %s", err) 70 } 71 72 env.t = t 73 env.config = config 74 env.DBProvider = dbProvider 75 env.config = config 76 env.cache = dbProvider.cache 77 env.sysNamespaces = sysNamespaces 78 } 79 80 func (env *testVDBEnv) closeAndReopen() { 81 env.DBProvider.Close() 82 dbProvider, _ := NewVersionedDBProvider(env.config, &disabled.Provider{}, env.sysNamespaces) 83 env.DBProvider = dbProvider 84 env.cache = dbProvider.cache 85 } 86 87 // Cleanup drops the test couch databases and closes the db provider 88 func (env *testVDBEnv) cleanup() { 89 env.t.Logf("Cleaningup TestVDBEnv") 90 if env.DBProvider != nil { 91 env.DBProvider.Close() 92 } 93 env.couchDBEnv.cleanup(env.config) 94 require.NoError(env.t, os.RemoveAll(env.config.RedoLogPath)) 95 } 96 97 // testVDBEnv provides a couch db for testing 98 type testCouchDBEnv struct { 99 t *testing.T 100 couchAddress string 101 cleanupCouchDB func() 102 } 103 104 // startCouchDB starts external couchDB resources for testCouchDBEnv. 105 func (env *testCouchDBEnv) startCouchDB(t *testing.T) { 106 if env.couchAddress != "" { 107 return 108 } 109 env.t = t 110 env.couchAddress, env.cleanupCouchDB = StartCouchDB(t, nil) 111 } 112 113 // stopCouchDB stops external couchDB resources. 114 func (env *testCouchDBEnv) stopCouchDB() { 115 if env.couchAddress != "" { 116 env.cleanupCouchDB() 117 } 118 } 119 120 func (env *testCouchDBEnv) cleanup(config *ledger.CouchDBConfig) { 121 err := DropApplicationDBs(config) 122 require.NoError(env.t, err) 123 } 124 125 // we create two CouchDB instances/containers---one is used to test the 126 // functionality of the versionedDB and another for testing the CouchDB 127 // util functions. 128 var ( 129 vdbEnv = &testVDBEnv{} 130 couchDBEnv = &testCouchDBEnv{} 131 ) 132 133 func TestMain(m *testing.M) { 134 flogging.ActivateSpec("statecouchdb=debug") 135 136 rc := m.Run() 137 if vdbEnv.couchDBEnv != nil { 138 vdbEnv.couchDBEnv.stopCouchDB() 139 } 140 couchDBEnv.stopCouchDB() 141 os.Exit(rc) 142 } 143 144 func TestBasicRW(t *testing.T) { 145 vdbEnv.init(t, nil) 146 defer vdbEnv.cleanup() 147 148 commontests.TestBasicRW(t, vdbEnv.DBProvider) 149 } 150 151 // TestGetStateFromCache checks cache hits, cache misses, and cache 152 // updates during GetState call. 153 func TestGetStateFromCache(t *testing.T) { 154 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 155 defer vdbEnv.cleanup() 156 157 chainID := "testgetstatefromcache" 158 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 159 require.NoError(t, err) 160 161 // scenario 1: get state would receives a 162 // cache miss as the given key does not exist. 163 // As the key does not exist in the 164 // db also, get state call would not update 165 // the cache. 166 vv, err := db.GetState("ns", "key1") 167 require.NoError(t, err) 168 require.Nil(t, vv) 169 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns", "key1") 170 171 // scenario 2: get state would receive a cache hit. 172 // directly store an entry in the cache 173 cacheValue := &CacheValue{ 174 Value: []byte("value1"), 175 Metadata: []byte("meta1"), 176 Version: version.NewHeight(1, 1).ToBytes(), 177 AdditionalInfo: []byte("rev1"), 178 } 179 require.NoError(t, vdbEnv.cache.putState(chainID, "ns", "key1", cacheValue)) 180 181 vv, err = db.GetState("ns", "key1") 182 require.NoError(t, err) 183 expectedVV, err := constructVersionedValue(cacheValue) 184 require.NoError(t, err) 185 require.Equal(t, expectedVV, vv) 186 187 // scenario 3: get state would receives a 188 // cache miss as the given key does not present. 189 // The value associated with the key would be 190 // fetched from the database and the cache would 191 // be updated accordingly. 192 193 // store an entry in the db 194 batch := statedb.NewUpdateBatch() 195 vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)} 196 batch.PutValAndMetadata("lscc", "key1", vv2.Value, vv2.Metadata, vv2.Version) 197 savePoint := version.NewHeight(1, 2) 198 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 199 // Note that the ApplyUpdates() updates only the existing entry in the cache. Currently, the 200 // cache has only ns, key1 but we are storing lscc, key1. Hence, no changes would happen in the cache. 201 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "lscc", "key1") 202 203 // calling GetState() would update the cache 204 vv, err = db.GetState("lscc", "key1") 205 require.NoError(t, err) 206 require.Equal(t, vv2, vv) 207 208 // cache should have been updated with lscc, key1 209 nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("lscc") 210 require.NoError(t, err) 211 testExistInCache(t, nsdb, vdbEnv.cache, chainID, "lscc", "key1", vv2) 212 } 213 214 // TestGetVersionFromCache checks cache hits, cache misses, and 215 // updates during GetVersion call. 216 func TestGetVersionFromCache(t *testing.T) { 217 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 218 defer vdbEnv.cleanup() 219 220 chainID := "testgetstatefromcache" 221 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 222 require.NoError(t, err) 223 224 // scenario 1: get version would receives a 225 // cache miss as the given key does not exist. 226 // As the key does not exist in the 227 // db also, get version call would not update 228 // the cache. 229 ver, err := db.GetVersion("ns", "key1") 230 require.Nil(t, err) 231 require.Nil(t, ver) 232 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns", "key1") 233 234 // scenario 2: get version would receive a cache hit. 235 // directly store an entry in the cache 236 cacheValue := &CacheValue{ 237 Value: []byte("value1"), 238 Metadata: []byte("meta1"), 239 Version: version.NewHeight(1, 1).ToBytes(), 240 AdditionalInfo: []byte("rev1"), 241 } 242 require.NoError(t, vdbEnv.cache.putState(chainID, "ns", "key1", cacheValue)) 243 244 ver, err = db.GetVersion("ns", "key1") 245 require.NoError(t, err) 246 expectedVer, _, err := version.NewHeightFromBytes(cacheValue.Version) 247 require.NoError(t, err) 248 require.Equal(t, expectedVer, ver) 249 250 // scenario 3: get version would receives a 251 // cache miss as the given key does not present. 252 // The value associated with the key would be 253 // fetched from the database and the cache would 254 // be updated accordingly. 255 256 // store an entry in the db 257 batch := statedb.NewUpdateBatch() 258 vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)} 259 batch.PutValAndMetadata("lscc", "key1", vv2.Value, vv2.Metadata, vv2.Version) 260 savePoint := version.NewHeight(1, 2) 261 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 262 // Note that the ApplyUpdates() updates only the existing entry in the cache. Currently, the 263 // cache has only ns, key1 but we are storing lscc, key1. Hence, no changes would happen in the cache. 264 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "lscc", "key1") 265 266 // calling GetVersion() would update the cache 267 ver, err = db.GetVersion("lscc", "key1") 268 require.NoError(t, err) 269 require.Equal(t, vv2.Version, ver) 270 271 // cache should have been updated with lscc, key1 272 nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("lscc") 273 require.NoError(t, err) 274 testExistInCache(t, nsdb, vdbEnv.cache, chainID, "lscc", "key1", vv2) 275 } 276 277 // TestGetMultipleStatesFromCache checks cache hits, cache misses, 278 // and updates during GetStateMultipleKeys call. 279 func TestGetMultipleStatesFromCache(t *testing.T) { 280 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 281 defer vdbEnv.cleanup() 282 283 chainID := "testgetmultiplestatesfromcache" 284 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 285 require.NoError(t, err) 286 287 // scenario: given 5 keys, get multiple states find 288 // 2 keys in the cache. The remaining 2 keys would be fetched 289 // from the database and the cache would be updated. The last 290 // key is not present in the db and hence it won't be sent to 291 // the cache. 292 293 // key1 and key2 exist only in the cache 294 cacheValue1 := &CacheValue{ 295 Value: []byte("value1"), 296 Metadata: []byte("meta1"), 297 Version: version.NewHeight(1, 1).ToBytes(), 298 AdditionalInfo: []byte("rev1"), 299 } 300 require.NoError(t, vdbEnv.cache.putState(chainID, "ns", "key1", cacheValue1)) 301 cacheValue2 := &CacheValue{ 302 Value: []byte("value2"), 303 Metadata: []byte("meta2"), 304 Version: version.NewHeight(1, 1).ToBytes(), 305 AdditionalInfo: []byte("rev2"), 306 } 307 require.NoError(t, vdbEnv.cache.putState(chainID, "ns", "key2", cacheValue2)) 308 309 // key3 and key4 exist only in the db 310 batch := statedb.NewUpdateBatch() 311 vv3 := &statedb.VersionedValue{Value: []byte("value3"), Metadata: []byte("meta3"), Version: version.NewHeight(1, 1)} 312 batch.PutValAndMetadata("ns", "key3", vv3.Value, vv3.Metadata, vv3.Version) 313 vv4 := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 1)} 314 batch.PutValAndMetadata("ns", "key4", vv4.Value, vv4.Metadata, vv4.Version) 315 savePoint := version.NewHeight(1, 2) 316 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 317 318 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns", "key3") 319 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns", "key4") 320 321 // key5 does not exist at all while key3 and key4 does not exist in the cache 322 vvalues, err := db.GetStateMultipleKeys("ns", []string{"key1", "key2", "key3", "key4", "key5"}) 323 require.Nil(t, err) 324 vv1, err := constructVersionedValue(cacheValue1) 325 require.NoError(t, err) 326 vv2, err := constructVersionedValue(cacheValue2) 327 require.NoError(t, err) 328 require.Equal(t, []*statedb.VersionedValue{vv1, vv2, vv3, vv4, nil}, vvalues) 329 330 // cache should have been updated with key3 and key4 331 nsdb, err := db.(*VersionedDB).getNamespaceDBHandle("ns") 332 require.NoError(t, err) 333 testExistInCache(t, nsdb, vdbEnv.cache, chainID, "ns", "key3", vv3) 334 testExistInCache(t, nsdb, vdbEnv.cache, chainID, "ns", "key4", vv4) 335 } 336 337 // TestCacheUpdatesAfterCommit checks whether the cache is updated 338 // after a commit of a update batch. 339 func TestCacheUpdatesAfterCommit(t *testing.T) { 340 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 341 defer vdbEnv.cleanup() 342 343 chainID := "testcacheupdatesaftercommit" 344 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 345 require.NoError(t, err) 346 347 // scenario: cache has 4 keys while the commit operation 348 // updates 2 of those keys, delete the remaining 2 keys, and 349 // adds a new key. At the end of the commit operation, only 350 // those 2 keys should be present with the recent value 351 // in the cache and the new key should not be present in the cache. 352 353 // store 4 keys in the db 354 batch := statedb.NewUpdateBatch() 355 vv1 := &statedb.VersionedValue{Value: []byte("value1"), Metadata: []byte("meta1"), Version: version.NewHeight(1, 2)} 356 vv2 := &statedb.VersionedValue{Value: []byte("value2"), Metadata: []byte("meta2"), Version: version.NewHeight(1, 2)} 357 vv3 := &statedb.VersionedValue{Value: []byte("value3"), Metadata: []byte("meta3"), Version: version.NewHeight(1, 2)} 358 vv4 := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 2)} 359 360 batch.PutValAndMetadata("ns1", "key1", vv1.Value, vv1.Metadata, vv1.Version) 361 batch.PutValAndMetadata("ns1", "key2", vv2.Value, vv2.Metadata, vv2.Version) 362 batch.PutValAndMetadata("ns2", "key1", vv3.Value, vv3.Metadata, vv3.Version) 363 batch.PutValAndMetadata("ns2", "key2", vv4.Value, vv4.Metadata, vv4.Version) 364 savePoint := version.NewHeight(1, 5) 365 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 366 367 // key1, key2 in ns1 and ns2 would not be in cache 368 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns1", "key1") 369 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns1", "key2") 370 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key1") 371 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key2") 372 373 // add key1 and key2 from ns1 to the cache 374 _, err = db.GetState("ns1", "key1") 375 require.NoError(t, err) 376 _, err = db.GetState("ns1", "key2") 377 require.NoError(t, err) 378 // add key1 and key2 from ns2 to the cache 379 _, err = db.GetState("ns2", "key1") 380 require.NoError(t, err) 381 _, err = db.GetState("ns2", "key2") 382 require.NoError(t, err) 383 384 v, err := vdbEnv.cache.getState(chainID, "ns1", "key1") 385 require.NoError(t, err) 386 ns1key1rev := string(v.AdditionalInfo) 387 388 v, err = vdbEnv.cache.getState(chainID, "ns1", "key2") 389 require.NoError(t, err) 390 ns1key2rev := string(v.AdditionalInfo) 391 392 // update key1 and key2 in ns1. delete key1 and key2 in ns2. add a new key3 in ns2. 393 batch = statedb.NewUpdateBatch() 394 vv1Update := &statedb.VersionedValue{Value: []byte("new-value1"), Metadata: []byte("meta1"), Version: version.NewHeight(2, 2)} 395 vv2Update := &statedb.VersionedValue{Value: []byte("new-value2"), Metadata: []byte("meta2"), Version: version.NewHeight(2, 2)} 396 vv3Update := &statedb.VersionedValue{Version: version.NewHeight(2, 4)} 397 vv4Update := &statedb.VersionedValue{Version: version.NewHeight(2, 5)} 398 vv5 := &statedb.VersionedValue{Value: []byte("value5"), Metadata: []byte("meta5"), Version: version.NewHeight(1, 2)} 399 400 batch.PutValAndMetadata("ns1", "key1", vv1Update.Value, vv1Update.Metadata, vv1Update.Version) 401 batch.PutValAndMetadata("ns1", "key2", vv2Update.Value, vv2Update.Metadata, vv2Update.Version) 402 batch.Delete("ns2", "key1", vv3Update.Version) 403 batch.Delete("ns2", "key2", vv4Update.Version) 404 batch.PutValAndMetadata("ns2", "key3", vv5.Value, vv5.Metadata, vv5.Version) 405 savePoint = version.NewHeight(2, 5) 406 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 407 408 // cache should have only the update key1 and key2 in ns1 409 cacheValue, err := vdbEnv.cache.getState(chainID, "ns1", "key1") 410 require.NoError(t, err) 411 vv, err := constructVersionedValue(cacheValue) 412 require.NoError(t, err) 413 require.Equal(t, vv1Update, vv) 414 require.NotEqual(t, ns1key1rev, string(cacheValue.AdditionalInfo)) 415 416 cacheValue, err = vdbEnv.cache.getState(chainID, "ns1", "key2") 417 require.NoError(t, err) 418 vv, err = constructVersionedValue(cacheValue) 419 require.NoError(t, err) 420 require.Equal(t, vv2Update, vv) 421 require.NotEqual(t, ns1key2rev, string(cacheValue.AdditionalInfo)) 422 423 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key1") 424 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key2") 425 testDoesNotExistInCache(t, vdbEnv.cache, chainID, "ns2", "key3") 426 } 427 428 func TestMultiDBBasicRW(t *testing.T) { 429 vdbEnv.init(t, nil) 430 defer vdbEnv.cleanup() 431 432 commontests.TestMultiDBBasicRW(t, vdbEnv.DBProvider) 433 } 434 435 func TestDeletes(t *testing.T) { 436 vdbEnv.init(t, nil) 437 defer vdbEnv.cleanup() 438 439 commontests.TestDeletes(t, vdbEnv.DBProvider) 440 } 441 442 func TestIterator(t *testing.T) { 443 vdbEnv.init(t, nil) 444 defer vdbEnv.cleanup() 445 446 commontests.TestIterator(t, vdbEnv.DBProvider) 447 } 448 449 // The following tests are unique to couchdb, they are not used in leveldb 450 // query test 451 func TestQuery(t *testing.T) { 452 vdbEnv.init(t, nil) 453 defer vdbEnv.cleanup() 454 455 commontests.TestQuery(t, vdbEnv.DBProvider) 456 } 457 458 func TestGetStateMultipleKeys(t *testing.T) { 459 vdbEnv.init(t, nil) 460 defer vdbEnv.cleanup() 461 462 commontests.TestGetStateMultipleKeys(t, vdbEnv.DBProvider) 463 } 464 465 func TestGetVersion(t *testing.T) { 466 vdbEnv.init(t, nil) 467 defer vdbEnv.cleanup() 468 469 commontests.TestGetVersion(t, vdbEnv.DBProvider) 470 } 471 472 func TestSmallBatchSize(t *testing.T) { 473 vdbEnv.init(t, nil) 474 defer vdbEnv.cleanup() 475 476 commontests.TestSmallBatchSize(t, vdbEnv.DBProvider) 477 } 478 479 func TestBatchRetry(t *testing.T) { 480 vdbEnv.init(t, nil) 481 defer vdbEnv.cleanup() 482 483 commontests.TestBatchWithIndividualRetry(t, vdbEnv.DBProvider) 484 } 485 486 func TestValueAndMetadataWrites(t *testing.T) { 487 vdbEnv.init(t, nil) 488 defer vdbEnv.cleanup() 489 490 commontests.TestValueAndMetadataWrites(t, vdbEnv.DBProvider) 491 } 492 493 func TestPaginatedRangeQuery(t *testing.T) { 494 vdbEnv.init(t, nil) 495 defer vdbEnv.cleanup() 496 497 commontests.TestPaginatedRangeQuery(t, vdbEnv.DBProvider) 498 } 499 500 func TestRangeQuerySpecialCharacters(t *testing.T) { 501 vdbEnv.init(t, nil) 502 defer vdbEnv.cleanup() 503 504 commontests.TestRangeQuerySpecialCharacters(t, vdbEnv.DBProvider) 505 } 506 507 // TestUtilityFunctions tests utility functions 508 func TestUtilityFunctions(t *testing.T) { 509 vdbEnv.init(t, nil) 510 defer vdbEnv.cleanup() 511 512 db, err := vdbEnv.DBProvider.GetDBHandle("testutilityfunctions", nil) 513 require.NoError(t, err) 514 515 require.False(t, vdbEnv.DBProvider.BytesKeySupported()) 516 require.False(t, db.BytesKeySupported()) 517 518 // ValidateKeyValue should return nil for a valid key and value 519 err = db.ValidateKeyValue("testKey", []byte("Some random bytes")) 520 require.Nil(t, err) 521 522 // ValidateKeyValue should return an error for a key that is not a utf-8 valid string 523 err = db.ValidateKeyValue(string([]byte{0xff, 0xfe, 0xfd}), []byte("Some random bytes")) 524 require.Error(t, err, "ValidateKey should have thrown an error for an invalid utf-8 string") 525 526 // ValidateKeyValue should return an error for a key that is an empty string 527 require.EqualError(t, db.ValidateKeyValue("", []byte("validValue")), 528 "invalid key. Empty string is not supported as a key by couchdb") 529 530 reservedFields := []string{"~version", "_id", "_test"} 531 532 // ValidateKeyValue should return an error for a json value that contains one of the reserved fields 533 // at the top level 534 for _, reservedField := range reservedFields { 535 testVal := fmt.Sprintf(`{"%s":"dummyVal"}`, reservedField) 536 err = db.ValidateKeyValue("testKey", []byte(testVal)) 537 require.Error(t, err, fmt.Sprintf( 538 "ValidateKey should have thrown an error for a json value %s, as contains one of the reserved fields", testVal)) 539 } 540 541 // ValidateKeyValue should not return an error for a json value that contains one of the reserved fields 542 // if not at the top level 543 for _, reservedField := range reservedFields { 544 testVal := fmt.Sprintf(`{"data.%s":"dummyVal"}`, reservedField) 545 err = db.ValidateKeyValue("testKey", []byte(testVal)) 546 require.NoError(t, err, fmt.Sprintf( 547 "ValidateKey should not have thrown an error the json value %s since the reserved field was not at the top level", testVal)) 548 } 549 550 // ValidateKeyValue should return an error for a key that begins with an underscore 551 err = db.ValidateKeyValue("_testKey", []byte("testValue")) 552 require.Error(t, err, "ValidateKey should have thrown an error for a key that begins with an underscore") 553 } 554 555 // TestInvalidJSONFields tests for invalid JSON fields 556 func TestInvalidJSONFields(t *testing.T) { 557 vdbEnv.init(t, nil) 558 defer vdbEnv.cleanup() 559 560 db, err := vdbEnv.DBProvider.GetDBHandle("testinvalidfields", nil) 561 require.NoError(t, err) 562 563 require.NoError(t, db.Open()) 564 defer db.Close() 565 566 batch := statedb.NewUpdateBatch() 567 jsonValue1 := `{"_id":"key1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}` 568 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 569 570 savePoint := version.NewHeight(1, 2) 571 err = db.ApplyUpdates(batch, savePoint) 572 require.Error(t, err, "Invalid field _id should have thrown an error") 573 574 batch = statedb.NewUpdateBatch() 575 jsonValue1 = `{"_rev":"rev1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}` 576 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 577 578 savePoint = version.NewHeight(1, 2) 579 err = db.ApplyUpdates(batch, savePoint) 580 require.Error(t, err, "Invalid field _rev should have thrown an error") 581 582 batch = statedb.NewUpdateBatch() 583 jsonValue1 = `{"_deleted":"true","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}` 584 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 585 586 savePoint = version.NewHeight(1, 2) 587 err = db.ApplyUpdates(batch, savePoint) 588 require.Error(t, err, "Invalid field _deleted should have thrown an error") 589 590 batch = statedb.NewUpdateBatch() 591 jsonValue1 = `{"~version":"v1","asset_name":"marble1","color":"blue","size":1,"owner":"tom"}` 592 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 593 594 savePoint = version.NewHeight(1, 2) 595 err = db.ApplyUpdates(batch, savePoint) 596 require.Error(t, err, "Invalid field ~version should have thrown an error") 597 } 598 599 func TestDebugFunctions(t *testing.T) { 600 // Test printCompositeKeys 601 // initialize a key list 602 loadKeys := []*statedb.CompositeKey{} 603 // create a composite key and add to the key list 604 compositeKey3 := statedb.CompositeKey{Namespace: "ns", Key: "key3"} 605 loadKeys = append(loadKeys, &compositeKey3) 606 compositeKey4 := statedb.CompositeKey{Namespace: "ns", Key: "key4"} 607 loadKeys = append(loadKeys, &compositeKey4) 608 require.Equal(t, "[ns,key3],[ns,key4]", printCompositeKeys(loadKeys)) 609 } 610 611 func TestHandleChaincodeDeploy(t *testing.T) { 612 vdbEnv.init(t, nil) 613 defer vdbEnv.cleanup() 614 615 db, err := vdbEnv.DBProvider.GetDBHandle("testinit", nil) 616 require.NoError(t, err) 617 require.NoError(t, db.Open()) 618 defer db.Close() 619 batch := statedb.NewUpdateBatch() 620 621 jsonValue1 := `{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}` 622 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 623 jsonValue2 := `{"asset_name": "marble2","color": "blue","size": 2,"owner": "jerry"}` 624 batch.Put("ns1", "key2", []byte(jsonValue2), version.NewHeight(1, 2)) 625 jsonValue3 := `{"asset_name": "marble3","color": "blue","size": 3,"owner": "fred"}` 626 batch.Put("ns1", "key3", []byte(jsonValue3), version.NewHeight(1, 3)) 627 jsonValue4 := `{"asset_name": "marble4","color": "blue","size": 4,"owner": "martha"}` 628 batch.Put("ns1", "key4", []byte(jsonValue4), version.NewHeight(1, 4)) 629 jsonValue5 := `{"asset_name": "marble5","color": "blue","size": 5,"owner": "fred"}` 630 batch.Put("ns1", "key5", []byte(jsonValue5), version.NewHeight(1, 5)) 631 jsonValue6 := `{"asset_name": "marble6","color": "blue","size": 6,"owner": "elaine"}` 632 batch.Put("ns1", "key6", []byte(jsonValue6), version.NewHeight(1, 6)) 633 jsonValue7 := `{"asset_name": "marble7","color": "blue","size": 7,"owner": "fred"}` 634 batch.Put("ns1", "key7", []byte(jsonValue7), version.NewHeight(1, 7)) 635 jsonValue8 := `{"asset_name": "marble8","color": "blue","size": 8,"owner": "elaine"}` 636 batch.Put("ns1", "key8", []byte(jsonValue8), version.NewHeight(1, 8)) 637 jsonValue9 := `{"asset_name": "marble9","color": "green","size": 9,"owner": "fred"}` 638 batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9)) 639 jsonValue10 := `{"asset_name": "marble10","color": "green","size": 10,"owner": "mary"}` 640 batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10)) 641 jsonValue11 := `{"asset_name": "marble11","color": "cyan","size": 1000007,"owner": "joe"}` 642 batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11)) 643 644 // add keys for a separate namespace 645 batch.Put("ns2", "key1", []byte(jsonValue1), version.NewHeight(1, 12)) 646 batch.Put("ns2", "key2", []byte(jsonValue2), version.NewHeight(1, 13)) 647 batch.Put("ns2", "key3", []byte(jsonValue3), version.NewHeight(1, 14)) 648 batch.Put("ns2", "key4", []byte(jsonValue4), version.NewHeight(1, 15)) 649 batch.Put("ns2", "key5", []byte(jsonValue5), version.NewHeight(1, 16)) 650 batch.Put("ns2", "key6", []byte(jsonValue6), version.NewHeight(1, 17)) 651 batch.Put("ns2", "key7", []byte(jsonValue7), version.NewHeight(1, 18)) 652 batch.Put("ns2", "key8", []byte(jsonValue8), version.NewHeight(1, 19)) 653 batch.Put("ns2", "key9", []byte(jsonValue9), version.NewHeight(1, 20)) 654 batch.Put("ns2", "key10", []byte(jsonValue10), version.NewHeight(1, 21)) 655 656 savePoint := version.NewHeight(2, 22) 657 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 658 659 indexData := map[string][]byte{ 660 "META-INF/statedb/couchdb/indexes/indexColorSortName.json": []byte(`{"index":{"fields":[{"color":"desc"}]},"ddoc":"indexColorSortName","name":"indexColorSortName","type":"json"}`), 661 "META-INF/statedb/couchdb/indexes/indexSizeSortName.json": []byte(`{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`), 662 "META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexCollMarbles.json": []byte(`{"index":{"fields":["docType","owner"]},"ddoc":"indexCollectionMarbles", "name":"indexCollectionMarbles","type":"json"}`), 663 "META-INF/statedb/couchdb/collections/collectionMarblesPrivateDetails/indexes/indexCollPrivDetails.json": []byte(`{"index":{"fields":["docType","price"]},"ddoc":"indexPrivateDetails", "name":"indexPrivateDetails","type":"json"}`), 664 } 665 666 // Create a query 667 queryString := `{"selector":{"owner":"fred"}}` 668 669 _, err = db.ExecuteQuery("ns1", queryString) 670 require.NoError(t, err) 671 672 // Create a query with a sort 673 queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}` 674 675 _, err = db.ExecuteQuery("ns1", queryString) 676 require.Error(t, err, "Error should have been thrown for a missing index") 677 678 indexCapable, ok := db.(statedb.IndexCapable) 679 if !ok { 680 t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`") 681 } 682 require.NoError(t, indexCapable.ProcessIndexesForChaincodeDeploy("ns1", indexData)) 683 684 queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}` 685 queryUsingIndex := func() bool { 686 _, err = db.ExecuteQuery("ns1", queryString) 687 return err == nil 688 } 689 require.Eventually(t, queryUsingIndex, 2*time.Second, 100*time.Millisecond, "error executing query with sort") 690 691 // Query namespace "ns2", index is only created in "ns1". This should return an error. 692 _, err = db.ExecuteQuery("ns2", queryString) 693 require.Error(t, err, "Error should have been thrown for a missing index") 694 } 695 696 func TestTryCastingToJSON(t *testing.T) { 697 sampleJSON := []byte(`{"a":"A", "b":"B"}`) 698 isJSON, jsonVal := tryCastingToJSON(sampleJSON) 699 require.True(t, isJSON) 700 require.Equal(t, "A", jsonVal["a"]) 701 require.Equal(t, "B", jsonVal["b"]) 702 703 sampleNonJSON := []byte(`This is not a json`) 704 isJSON, _ = tryCastingToJSON(sampleNonJSON) 705 require.False(t, isJSON) 706 } 707 708 func TestIndexDeploymentWithOrderAndBadSyntax(t *testing.T) { 709 channelName := "ch1" 710 vdbEnv.init(t, nil) 711 defer vdbEnv.cleanup() 712 db, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 713 require.NoError(t, err) 714 require.NoError(t, db.Open()) 715 defer db.Close() 716 717 batch := statedb.NewUpdateBatch() 718 batch.Put("ns1", "key1", []byte(`{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}`), version.NewHeight(1, 1)) 719 batch.Put("ns1", "key2", []byte(`{"asset_name": "marble2","color": "blue","size": 2,"owner": "jerry"}`), version.NewHeight(1, 2)) 720 721 indexCapable, ok := db.(statedb.IndexCapable) 722 if !ok { 723 t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`") 724 } 725 726 badSyntaxFileContent := `{"index":{"fields": This is a bad json}` 727 indexData := map[string][]byte{ 728 "META-INF/statedb/couchdb/indexes/indexColorSortName.json": []byte(`{"index":{"fields":[{"color":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`), 729 "META-INF/statedb/couchdb/indexes/indexSizeSortName.json": []byte(`{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`), 730 "META-INF/statedb/couchdb/indexes/badSyntax.json": []byte(badSyntaxFileContent), 731 "META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexCollMarbles.json": []byte(`{"index":{"fields":["docType","owner"]},"ddoc":"indexCollectionMarbles", "name":"indexCollectionMarbles","type":"json"}`), 732 } 733 734 // as the indexes are sorted by file names, the order of index processing would be 735 // (1) indexCollMarbles.json, (2) badSyntax.json, (3) indexColorSortName, (4) indexSizeSortName. 736 // As the indexColorSortName.json and indexSizeSortName has the same index name but different 737 // index fields, the later would replace the former, i.e., index would be created on size field 738 // rather than the color field. Further, the index with a bad syntax would not stop the processing 739 // of other valid indexes. 740 require.NoError(t, indexCapable.ProcessIndexesForChaincodeDeploy("ns1", indexData)) 741 742 queryString := `{"selector":{"owner":"fred"}, "sort": [{"docType": "desc"}]}` 743 queryUsingIndex := func() bool { 744 _, err = db.ExecuteQuery("ns1", queryString) 745 return err == nil 746 } 747 require.Eventually(t, queryUsingIndex, 2*time.Second, 100*time.Millisecond, "error executing query with sort") 748 749 queryString = `{"selector":{"owner":"fred"}, "sort": [{"size": "desc"}]}` 750 queryUsingIndex = func() bool { 751 _, err = db.ExecuteQuery("ns1", queryString) 752 return err == nil 753 } 754 require.Eventually(t, queryUsingIndex, 2*time.Second, 100*time.Millisecond, "error executing query with sort") 755 756 // though the indexColorSortName.json is processed before indexSizeSortName.json as per the order, 757 // the later would replace the former as the index names are the same. Hence, a query using the color 758 // field in sort should fail. 759 queryString = `{"selector":{"owner":"fred"}, "sort": [{"color": "desc"}]}` 760 queryUsingIndex = func() bool { 761 _, err = db.ExecuteQuery("ns1", queryString) 762 return err == nil 763 } 764 require.Never(t, queryUsingIndex, 2*time.Second, 100*time.Millisecond, "error should have occurred as there is no index on color field") 765 } 766 767 func TestIsBulkOptimizable(t *testing.T) { 768 var db statedb.VersionedDB = &VersionedDB{} 769 _, ok := db.(statedb.BulkOptimizable) 770 if !ok { 771 t.Fatal("state couch db is expected to implement interface statedb.BulkOptimizable") 772 } 773 } 774 775 func printCompositeKeys(keys []*statedb.CompositeKey) string { 776 compositeKeyString := []string{} 777 for _, key := range keys { 778 compositeKeyString = append(compositeKeyString, "["+key.Namespace+","+key.Key+"]") 779 } 780 return strings.Join(compositeKeyString, ",") 781 } 782 783 // TestPaginatedQuery tests queries with pagination 784 func TestPaginatedQuery(t *testing.T) { 785 vdbEnv.init(t, nil) 786 defer vdbEnv.cleanup() 787 788 db, err := vdbEnv.DBProvider.GetDBHandle("testpaginatedquery", nil) 789 require.NoError(t, err) 790 require.NoError(t, db.Open()) 791 defer db.Close() 792 793 batch := statedb.NewUpdateBatch() 794 jsonValue1 := `{"asset_name": "marble1","color": "blue","size": 1,"owner": "tom"}` 795 batch.Put("ns1", "key1", []byte(jsonValue1), version.NewHeight(1, 1)) 796 jsonValue2 := `{"asset_name": "marble2","color": "red","size": 2,"owner": "jerry"}` 797 batch.Put("ns1", "key2", []byte(jsonValue2), version.NewHeight(1, 2)) 798 jsonValue3 := `{"asset_name": "marble3","color": "red","size": 3,"owner": "fred"}` 799 batch.Put("ns1", "key3", []byte(jsonValue3), version.NewHeight(1, 3)) 800 jsonValue4 := `{"asset_name": "marble4","color": "red","size": 4,"owner": "martha"}` 801 batch.Put("ns1", "key4", []byte(jsonValue4), version.NewHeight(1, 4)) 802 jsonValue5 := `{"asset_name": "marble5","color": "blue","size": 5,"owner": "fred"}` 803 batch.Put("ns1", "key5", []byte(jsonValue5), version.NewHeight(1, 5)) 804 jsonValue6 := `{"asset_name": "marble6","color": "red","size": 6,"owner": "elaine"}` 805 batch.Put("ns1", "key6", []byte(jsonValue6), version.NewHeight(1, 6)) 806 jsonValue7 := `{"asset_name": "marble7","color": "blue","size": 7,"owner": "fred"}` 807 batch.Put("ns1", "key7", []byte(jsonValue7), version.NewHeight(1, 7)) 808 jsonValue8 := `{"asset_name": "marble8","color": "red","size": 8,"owner": "elaine"}` 809 batch.Put("ns1", "key8", []byte(jsonValue8), version.NewHeight(1, 8)) 810 jsonValue9 := `{"asset_name": "marble9","color": "green","size": 9,"owner": "fred"}` 811 batch.Put("ns1", "key9", []byte(jsonValue9), version.NewHeight(1, 9)) 812 jsonValue10 := `{"asset_name": "marble10","color": "green","size": 10,"owner": "mary"}` 813 batch.Put("ns1", "key10", []byte(jsonValue10), version.NewHeight(1, 10)) 814 815 jsonValue11 := `{"asset_name": "marble11","color": "cyan","size": 11,"owner": "joe"}` 816 batch.Put("ns1", "key11", []byte(jsonValue11), version.NewHeight(1, 11)) 817 jsonValue12 := `{"asset_name": "marble12","color": "red","size": 12,"owner": "martha"}` 818 batch.Put("ns1", "key12", []byte(jsonValue12), version.NewHeight(1, 4)) 819 jsonValue13 := `{"asset_name": "marble13","color": "red","size": 13,"owner": "james"}` 820 batch.Put("ns1", "key13", []byte(jsonValue13), version.NewHeight(1, 4)) 821 jsonValue14 := `{"asset_name": "marble14","color": "red","size": 14,"owner": "fred"}` 822 batch.Put("ns1", "key14", []byte(jsonValue14), version.NewHeight(1, 4)) 823 jsonValue15 := `{"asset_name": "marble15","color": "red","size": 15,"owner": "mary"}` 824 batch.Put("ns1", "key15", []byte(jsonValue15), version.NewHeight(1, 4)) 825 jsonValue16 := `{"asset_name": "marble16","color": "red","size": 16,"owner": "robert"}` 826 batch.Put("ns1", "key16", []byte(jsonValue16), version.NewHeight(1, 4)) 827 jsonValue17 := `{"asset_name": "marble17","color": "red","size": 17,"owner": "alan"}` 828 batch.Put("ns1", "key17", []byte(jsonValue17), version.NewHeight(1, 4)) 829 jsonValue18 := `{"asset_name": "marble18","color": "red","size": 18,"owner": "elaine"}` 830 batch.Put("ns1", "key18", []byte(jsonValue18), version.NewHeight(1, 4)) 831 jsonValue19 := `{"asset_name": "marble19","color": "red","size": 19,"owner": "alan"}` 832 batch.Put("ns1", "key19", []byte(jsonValue19), version.NewHeight(1, 4)) 833 jsonValue20 := `{"asset_name": "marble20","color": "red","size": 20,"owner": "elaine"}` 834 batch.Put("ns1", "key20", []byte(jsonValue20), version.NewHeight(1, 4)) 835 836 jsonValue21 := `{"asset_name": "marble21","color": "cyan","size": 21,"owner": "joe"}` 837 batch.Put("ns1", "key21", []byte(jsonValue21), version.NewHeight(1, 11)) 838 jsonValue22 := `{"asset_name": "marble22","color": "red","size": 22,"owner": "martha"}` 839 batch.Put("ns1", "key22", []byte(jsonValue22), version.NewHeight(1, 4)) 840 jsonValue23 := `{"asset_name": "marble23","color": "blue","size": 23,"owner": "james"}` 841 batch.Put("ns1", "key23", []byte(jsonValue23), version.NewHeight(1, 4)) 842 jsonValue24 := `{"asset_name": "marble24","color": "red","size": 24,"owner": "fred"}` 843 batch.Put("ns1", "key24", []byte(jsonValue24), version.NewHeight(1, 4)) 844 jsonValue25 := `{"asset_name": "marble25","color": "red","size": 25,"owner": "mary"}` 845 batch.Put("ns1", "key25", []byte(jsonValue25), version.NewHeight(1, 4)) 846 jsonValue26 := `{"asset_name": "marble26","color": "red","size": 26,"owner": "robert"}` 847 batch.Put("ns1", "key26", []byte(jsonValue26), version.NewHeight(1, 4)) 848 jsonValue27 := `{"asset_name": "marble27","color": "green","size": 27,"owner": "alan"}` 849 batch.Put("ns1", "key27", []byte(jsonValue27), version.NewHeight(1, 4)) 850 jsonValue28 := `{"asset_name": "marble28","color": "red","size": 28,"owner": "elaine"}` 851 batch.Put("ns1", "key28", []byte(jsonValue28), version.NewHeight(1, 4)) 852 jsonValue29 := `{"asset_name": "marble29","color": "red","size": 29,"owner": "alan"}` 853 batch.Put("ns1", "key29", []byte(jsonValue29), version.NewHeight(1, 4)) 854 jsonValue30 := `{"asset_name": "marble30","color": "red","size": 30,"owner": "elaine"}` 855 batch.Put("ns1", "key30", []byte(jsonValue30), version.NewHeight(1, 4)) 856 857 jsonValue31 := `{"asset_name": "marble31","color": "cyan","size": 31,"owner": "joe"}` 858 batch.Put("ns1", "key31", []byte(jsonValue31), version.NewHeight(1, 11)) 859 jsonValue32 := `{"asset_name": "marble32","color": "red","size": 32,"owner": "martha"}` 860 batch.Put("ns1", "key32", []byte(jsonValue32), version.NewHeight(1, 4)) 861 jsonValue33 := `{"asset_name": "marble33","color": "red","size": 33,"owner": "james"}` 862 batch.Put("ns1", "key33", []byte(jsonValue33), version.NewHeight(1, 4)) 863 jsonValue34 := `{"asset_name": "marble34","color": "red","size": 34,"owner": "fred"}` 864 batch.Put("ns1", "key34", []byte(jsonValue34), version.NewHeight(1, 4)) 865 jsonValue35 := `{"asset_name": "marble35","color": "red","size": 35,"owner": "mary"}` 866 batch.Put("ns1", "key35", []byte(jsonValue35), version.NewHeight(1, 4)) 867 jsonValue36 := `{"asset_name": "marble36","color": "orange","size": 36,"owner": "robert"}` 868 batch.Put("ns1", "key36", []byte(jsonValue36), version.NewHeight(1, 4)) 869 jsonValue37 := `{"asset_name": "marble37","color": "red","size": 37,"owner": "alan"}` 870 batch.Put("ns1", "key37", []byte(jsonValue37), version.NewHeight(1, 4)) 871 jsonValue38 := `{"asset_name": "marble38","color": "yellow","size": 38,"owner": "elaine"}` 872 batch.Put("ns1", "key38", []byte(jsonValue38), version.NewHeight(1, 4)) 873 jsonValue39 := `{"asset_name": "marble39","color": "red","size": 39,"owner": "alan"}` 874 batch.Put("ns1", "key39", []byte(jsonValue39), version.NewHeight(1, 4)) 875 jsonValue40 := `{"asset_name": "marble40","color": "red","size": 40,"owner": "elaine"}` 876 batch.Put("ns1", "key40", []byte(jsonValue40), version.NewHeight(1, 4)) 877 878 savePoint := version.NewHeight(2, 22) 879 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 880 881 indexData := map[string][]byte{ 882 "META-INF/statedb/couchdb/indexes/indexSizeSortName.json": []byte(`{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`), 883 } 884 885 // Create a query 886 queryString := `{"selector":{"color":"red"}}` 887 888 _, err = db.ExecuteQuery("ns1", queryString) 889 require.NoError(t, err) 890 891 indexCapable, ok := db.(statedb.IndexCapable) 892 if !ok { 893 t.Fatalf("Couchdb state impl is expected to implement interface `statedb.IndexCapable`") 894 } 895 896 require.NoError(t, indexCapable.ProcessIndexesForChaincodeDeploy("ns1", indexData)) 897 // Sleep to allow time for index creation 898 time.Sleep(100 * time.Millisecond) 899 // Create a query with a sort 900 queryString = `{"selector":{"color":"red"}, "sort": [{"size": "asc"}]}` 901 902 // Query should complete without error 903 _, err = db.ExecuteQuery("ns1", queryString) 904 require.NoError(t, err) 905 906 // Test explicit paging 907 // Execute 3 page queries, there are 28 records with color red, use page size 10 908 returnKeys := []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"} 909 bookmark, err := executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys) 910 require.NoError(t, err) 911 returnKeys = []string{"key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29"} 912 bookmark, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys) 913 require.NoError(t, err) 914 915 returnKeys = []string{"key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"} 916 _, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys) 917 require.NoError(t, err) 918 919 // Test explicit paging 920 // Increase pagesize to 50, should return all values 921 returnKeys = []string{ 922 "key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", 923 "key16", "key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29", 924 "key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40", 925 } 926 _, err = executeQuery(t, db, "ns1", queryString, "", int32(50), returnKeys) 927 require.NoError(t, err) 928 929 // Test explicit paging 930 // Pagesize is 10, so all 28 records should be return in 3 "pages" 931 returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"} 932 bookmark, err = executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys) 933 require.NoError(t, err) 934 returnKeys = []string{"key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29"} 935 bookmark, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys) 936 require.NoError(t, err) 937 returnKeys = []string{"key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40"} 938 _, err = executeQuery(t, db, "ns1", queryString, bookmark, int32(10), returnKeys) 939 require.NoError(t, err) 940 941 // Test implicit paging 942 returnKeys = []string{ 943 "key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", 944 "key16", "key17", "key18", "key19", "key20", "key22", "key24", "key25", "key26", "key28", "key29", 945 "key30", "key32", "key33", "key34", "key35", "key37", "key39", "key40", 946 } 947 _, err = executeQuery(t, db, "ns1", queryString, "", int32(0), returnKeys) 948 require.NoError(t, err) 949 950 // pagesize greater than querysize will execute with implicit paging 951 returnKeys = []string{"key2", "key3", "key4", "key6", "key8", "key12", "key13", "key14", "key15", "key16"} 952 _, err = executeQuery(t, db, "ns1", queryString, "", int32(10), returnKeys) 953 require.NoError(t, err) 954 } 955 956 func executeQuery(t *testing.T, db statedb.VersionedDB, namespace, query, bookmark string, pageSize int32, returnKeys []string) (string, error) { 957 var itr statedb.ResultsIterator 958 var err error 959 960 if pageSize == int32(0) && bookmark == "" { 961 itr, err = db.ExecuteQuery(namespace, query) 962 if err != nil { 963 return "", err 964 } 965 } else { 966 itr, err = db.ExecuteQueryWithPagination(namespace, query, bookmark, pageSize) 967 if err != nil { 968 return "", err 969 } 970 } 971 972 // Verify the keys returned 973 commontests.TestItrWithoutClose(t, itr, returnKeys) 974 975 returnBookmark := "" 976 if queryResultItr, ok := itr.(statedb.QueryResultsIterator); ok { 977 returnBookmark = queryResultItr.GetBookmarkAndClose() 978 } 979 980 return returnBookmark, nil 981 } 982 983 func TestApplyUpdatesWithNilHeight(t *testing.T) { 984 vdbEnv.init(t, nil) 985 defer vdbEnv.cleanup() 986 commontests.TestApplyUpdatesWithNilHeight(t, vdbEnv.DBProvider) 987 } 988 989 func TestRangeScanWithCouchInternalDocsPresent(t *testing.T) { 990 vdbEnv.init(t, nil) 991 defer vdbEnv.cleanup() 992 db, err := vdbEnv.DBProvider.GetDBHandle("testrangescanfiltercouchinternaldocs", nil) 993 require.NoError(t, err) 994 couchDatabse, err := db.(*VersionedDB).getNamespaceDBHandle("ns") 995 require.NoError(t, err) 996 require.NoError(t, db.Open()) 997 defer db.Close() 998 _, err = couchDatabse.createIndex(`{ 999 "index" : {"fields" : ["asset_name"]}, 1000 "ddoc" : "indexAssetName", 1001 "name" : "indexAssetName", 1002 "type" : "json" 1003 }`) 1004 require.NoError(t, err) 1005 1006 _, err = couchDatabse.createIndex(`{ 1007 "index" : {"fields" : ["assetValue"]}, 1008 "ddoc" : "indexAssetValue", 1009 "name" : "indexAssetValue", 1010 "type" : "json" 1011 }`) 1012 require.NoError(t, err) 1013 1014 batch := statedb.NewUpdateBatch() 1015 for i := 1; i <= 3; i++ { 1016 keySmallerThanDesignDoc := fmt.Sprintf("Key-%d", i) 1017 keyGreaterThanDesignDoc := fmt.Sprintf("key-%d", i) 1018 jsonValue := fmt.Sprintf(`{"asset_name": "marble-%d"}`, i) 1019 batch.Put("ns", keySmallerThanDesignDoc, []byte(jsonValue), version.NewHeight(1, uint64(i))) 1020 batch.Put("ns", keyGreaterThanDesignDoc, []byte(jsonValue), version.NewHeight(1, uint64(i))) 1021 } 1022 require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(2, 2))) 1023 require.NoError(t, err) 1024 1025 // The Keys in db are in this order 1026 // Key-1, Key-2, Key-3,_design/indexAssetNam, _design/indexAssetValue, key-1, key-2, key-3 1027 // query different ranges and verify results 1028 s, err := newQueryScanner("ns", couchDatabse, "", 3, 3, "", "", "") 1029 require.NoError(t, err) 1030 assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2", "Key-3"}) 1031 require.Equal(t, "key-1", s.queryDefinition.startKey) 1032 1033 s, err = newQueryScanner("ns", couchDatabse, "", 4, 4, "", "", "") 1034 require.NoError(t, err) 1035 assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2", "Key-3", "key-1"}) 1036 require.Equal(t, "key-2", s.queryDefinition.startKey) 1037 1038 s, err = newQueryScanner("ns", couchDatabse, "", 2, 2, "", "", "") 1039 require.NoError(t, err) 1040 assertQueryResults(t, s.resultsInfo.results, []string{"Key-1", "Key-2"}) 1041 require.Equal(t, "Key-3", s.queryDefinition.startKey) 1042 require.NoError(t, s.getNextStateRangeScanResults()) 1043 assertQueryResults(t, s.resultsInfo.results, []string{"Key-3", "key-1"}) 1044 require.Equal(t, "key-2", s.queryDefinition.startKey) 1045 1046 s, err = newQueryScanner("ns", couchDatabse, "", 2, 2, "", "_", "") 1047 require.NoError(t, err) 1048 assertQueryResults(t, s.resultsInfo.results, []string{"key-1", "key-2"}) 1049 require.Equal(t, "key-3", s.queryDefinition.startKey) 1050 } 1051 1052 func assertQueryResults(t *testing.T, results []*queryResult, expectedIds []string) { 1053 var actualIds []string 1054 for _, res := range results { 1055 actualIds = append(actualIds, res.id) 1056 } 1057 require.Equal(t, expectedIds, actualIds) 1058 } 1059 1060 func TestFormatCheck(t *testing.T) { 1061 testCases := []struct { 1062 dataFormat string // precondition 1063 dataExists bool // precondition 1064 expectedFormat string // postcondition 1065 expectedErr *dataformat.ErrFormatMismatch // postcondition 1066 }{ 1067 { 1068 dataFormat: "", 1069 dataExists: true, 1070 expectedErr: &dataformat.ErrFormatMismatch{ 1071 DBInfo: "CouchDB for state database", 1072 Format: "", 1073 ExpectedFormat: "2.0", 1074 }, 1075 expectedFormat: "does not matter as the test should not reach to check this", 1076 }, 1077 1078 { 1079 dataFormat: "", 1080 dataExists: false, 1081 expectedErr: nil, 1082 expectedFormat: dataformat.CurrentFormat, 1083 }, 1084 1085 { 1086 dataFormat: dataformat.CurrentFormat, 1087 dataExists: false, 1088 expectedFormat: dataformat.CurrentFormat, 1089 expectedErr: nil, 1090 }, 1091 1092 { 1093 dataFormat: dataformat.CurrentFormat, 1094 dataExists: true, 1095 expectedFormat: dataformat.CurrentFormat, 1096 expectedErr: nil, 1097 }, 1098 1099 { 1100 dataFormat: "3.0", 1101 dataExists: true, 1102 expectedErr: &dataformat.ErrFormatMismatch{ 1103 DBInfo: "CouchDB for state database", 1104 Format: "3.0", 1105 ExpectedFormat: dataformat.CurrentFormat, 1106 }, 1107 expectedFormat: "does not matter as the test should not reach to check this", 1108 }, 1109 } 1110 1111 vdbEnv.init(t, nil) 1112 for i, testCase := range testCases { 1113 t.Run( 1114 fmt.Sprintf("testCase %d", i), 1115 func(t *testing.T) { 1116 testFormatCheck(t, testCase.dataFormat, testCase.dataExists, testCase.expectedErr, testCase.expectedFormat, vdbEnv) 1117 }) 1118 } 1119 } 1120 1121 func testFormatCheck(t *testing.T, dataFormat string, dataExists bool, expectedErr *dataformat.ErrFormatMismatch, expectedFormat string, vdbEnv *testVDBEnv) { 1122 redoPath, err := ioutil.TempDir("", "redoPath") 1123 require.NoError(t, err) 1124 defer os.RemoveAll(redoPath) 1125 config := &ledger.CouchDBConfig{ 1126 Address: vdbEnv.couchDBEnv.couchAddress, 1127 Username: "admin", 1128 Password: "adminpw", 1129 MaxRetries: 3, 1130 MaxRetriesOnStartup: 20, 1131 RequestTimeout: 35 * time.Second, 1132 RedoLogPath: redoPath, 1133 } 1134 dbProvider, err := NewVersionedDBProvider(config, &disabled.Provider{}, nil) 1135 require.NoError(t, err) 1136 1137 // create preconditions for test 1138 if dataExists { 1139 db, err := dbProvider.GetDBHandle("testns", nil) 1140 require.NoError(t, err) 1141 batch := statedb.NewUpdateBatch() 1142 batch.Put("testns", "testkey", []byte("testVal"), version.NewHeight(1, 1)) 1143 require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(1, 1))) 1144 } 1145 if dataFormat == "" { 1146 err := dropDB(dbProvider.couchInstance, fabricInternalDBName) 1147 require.NoError(t, err) 1148 } else { 1149 require.NoError(t, writeDataFormatVersion(dbProvider.couchInstance, dataFormat)) 1150 } 1151 dbProvider.Close() 1152 defer func() { 1153 require.NoError(t, DropApplicationDBs(vdbEnv.config)) 1154 }() 1155 1156 // close and reopen with preconditions set and check the expected behavior 1157 dbProvider, err = NewVersionedDBProvider(config, &disabled.Provider{}, nil) 1158 if expectedErr != nil { 1159 require.Equal(t, expectedErr, err) 1160 return 1161 } 1162 require.NoError(t, err) 1163 defer func() { 1164 if dbProvider != nil { 1165 dbProvider.Close() 1166 } 1167 }() 1168 format, err := readDataformatVersion(dbProvider.couchInstance) 1169 require.NoError(t, err) 1170 require.Equal(t, expectedFormat, format) 1171 } 1172 1173 func testDoesNotExistInCache(t *testing.T, cache *cache, chainID, ns, key string) { 1174 cacheValue, err := cache.getState(chainID, ns, key) 1175 require.NoError(t, err) 1176 require.Nil(t, cacheValue) 1177 } 1178 1179 func testExistInCache(t *testing.T, db *couchDatabase, cache *cache, chainID, ns, key string, expectedVV *statedb.VersionedValue) { 1180 cacheValue, err := cache.getState(chainID, ns, key) 1181 require.NoError(t, err) 1182 vv, err := constructVersionedValue(cacheValue) 1183 require.NoError(t, err) 1184 require.Equal(t, expectedVV, vv) 1185 metadata, err := retrieveNsMetadata(db, []string{key}) 1186 require.NoError(t, err) 1187 require.Equal(t, metadata[0].Rev, string(cacheValue.AdditionalInfo)) 1188 } 1189 1190 func TestLoadCommittedVersion(t *testing.T) { 1191 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 1192 defer vdbEnv.cleanup() 1193 1194 chainID := "testloadcommittedversion" 1195 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 1196 require.NoError(t, err) 1197 1198 // scenario: state cache has (ns1, key1), (ns1, key2), 1199 // and (ns2, key1) but misses (ns2, key2). The 1200 // LoadCommittedVersions will fetch the first 1201 // three keys from the state cache and the remaining one from 1202 // the db. To ensure that, the db contains only 1203 // the missing key (ns2, key2). 1204 1205 // store (ns1, key1), (ns1, key2), (ns2, key1) in the state cache 1206 cacheValue := &CacheValue{ 1207 Value: []byte("value1"), 1208 Metadata: []byte("meta1"), 1209 Version: version.NewHeight(1, 1).ToBytes(), 1210 AdditionalInfo: []byte("rev1"), 1211 } 1212 require.NoError(t, vdbEnv.cache.putState(chainID, "ns1", "key1", cacheValue)) 1213 1214 cacheValue = &CacheValue{ 1215 Value: []byte("value2"), 1216 Metadata: []byte("meta2"), 1217 Version: version.NewHeight(1, 2).ToBytes(), 1218 AdditionalInfo: []byte("rev2"), 1219 } 1220 require.NoError(t, vdbEnv.cache.putState(chainID, "ns1", "key2", cacheValue)) 1221 1222 cacheValue = &CacheValue{ 1223 Value: []byte("value3"), 1224 Metadata: []byte("meta3"), 1225 Version: version.NewHeight(1, 3).ToBytes(), 1226 AdditionalInfo: []byte("rev3"), 1227 } 1228 require.NoError(t, vdbEnv.cache.putState(chainID, "ns2", "key1", cacheValue)) 1229 1230 // store (ns2, key2) in the db 1231 batch := statedb.NewUpdateBatch() 1232 vv := &statedb.VersionedValue{Value: []byte("value4"), Metadata: []byte("meta4"), Version: version.NewHeight(1, 4)} 1233 batch.PutValAndMetadata("ns2", "key2", vv.Value, vv.Metadata, vv.Version) 1234 savePoint := version.NewHeight(2, 2) 1235 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 1236 1237 // version cache should be empty 1238 ver, ok := db.(*VersionedDB).GetCachedVersion("ns1", "key1") 1239 require.Nil(t, ver) 1240 require.False(t, ok) 1241 ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key2") 1242 require.Nil(t, ver) 1243 require.False(t, ok) 1244 ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key1") 1245 require.Nil(t, ver) 1246 require.False(t, ok) 1247 ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key2") 1248 require.Nil(t, ver) 1249 require.False(t, ok) 1250 1251 keys := []*statedb.CompositeKey{ 1252 { 1253 Namespace: "ns1", 1254 Key: "key1", 1255 }, 1256 { 1257 Namespace: "ns1", 1258 Key: "key2", 1259 }, 1260 { 1261 Namespace: "ns2", 1262 Key: "key1", 1263 }, 1264 { 1265 Namespace: "ns2", 1266 Key: "key2", 1267 }, 1268 } 1269 1270 require.NoError(t, db.(*VersionedDB).LoadCommittedVersions(keys)) 1271 1272 ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key1") 1273 require.Equal(t, version.NewHeight(1, 1), ver) 1274 require.True(t, ok) 1275 ver, ok = db.(*VersionedDB).GetCachedVersion("ns1", "key2") 1276 require.Equal(t, version.NewHeight(1, 2), ver) 1277 require.True(t, ok) 1278 ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key1") 1279 require.Equal(t, version.NewHeight(1, 3), ver) 1280 require.True(t, ok) 1281 ver, ok = db.(*VersionedDB).GetCachedVersion("ns2", "key2") 1282 require.Equal(t, version.NewHeight(1, 4), ver) 1283 require.True(t, ok) 1284 } 1285 1286 func TestMissingRevisionRetrievalFromDB(t *testing.T) { 1287 vdbEnv.init(t, nil) 1288 defer vdbEnv.cleanup() 1289 chainID := "testmissingrevisionfromdb" 1290 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 1291 require.NoError(t, err) 1292 1293 // store key1, key2, key3 to the DB 1294 batch := statedb.NewUpdateBatch() 1295 vv1 := statedb.VersionedValue{Value: []byte("value1"), Version: version.NewHeight(1, 1)} 1296 vv2 := statedb.VersionedValue{Value: []byte("value2"), Version: version.NewHeight(1, 2)} 1297 vv3 := statedb.VersionedValue{Value: []byte("value3"), Version: version.NewHeight(1, 3)} 1298 batch.Put("ns1", "key1", vv1.Value, vv1.Version) 1299 batch.Put("ns1", "key2", vv2.Value, vv2.Version) 1300 batch.Put("ns1", "key3", vv3.Value, vv3.Version) 1301 savePoint := version.NewHeight(2, 5) 1302 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 1303 1304 // retrieve the versions of key1, key2, and key3 1305 revisions := make(map[string]string) 1306 require.NoError(t, db.(*VersionedDB).addMissingRevisionsFromDB("ns1", []string{"key1", "key2", "key3"}, revisions)) 1307 require.Equal(t, 3, len(revisions)) 1308 1309 // update key1 and key2 but not key3 1310 batch = statedb.NewUpdateBatch() 1311 vv4 := statedb.VersionedValue{Value: []byte("value1"), Version: version.NewHeight(1, 1)} 1312 vv5 := statedb.VersionedValue{Value: []byte("value2"), Version: version.NewHeight(1, 2)} 1313 batch.Put("ns1", "key1", vv4.Value, vv4.Version) 1314 batch.Put("ns1", "key2", vv5.Value, vv5.Version) 1315 savePoint = version.NewHeight(3, 5) 1316 require.NoError(t, db.ApplyUpdates(batch, savePoint)) 1317 1318 // for key3, the revision should be the same but not for key1 and key2 1319 newRevisions := make(map[string]string) 1320 require.NoError(t, db.(*VersionedDB).addMissingRevisionsFromDB("ns1", []string{"key1", "key2", "key3"}, newRevisions)) 1321 require.Equal(t, 3, len(newRevisions)) 1322 require.NotEqual(t, revisions["key1"], newRevisions["key1"]) 1323 require.NotEqual(t, revisions["key2"], newRevisions["key2"]) 1324 require.Equal(t, revisions["key3"], newRevisions["key3"]) 1325 } 1326 1327 func TestMissingRevisionRetrievalFromCache(t *testing.T) { 1328 vdbEnv.init(t, []string{"lscc", "_lifecycle"}) 1329 defer vdbEnv.cleanup() 1330 1331 chainID := "testmissingrevisionfromcache" 1332 db, err := vdbEnv.DBProvider.GetDBHandle(chainID, nil) 1333 require.NoError(t, err) 1334 1335 // scenario 1: missing from cache. 1336 revisions := make(map[string]string) 1337 stillMissingKeys, err := db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions) 1338 require.NoError(t, err) 1339 require.Equal(t, []string{"key1", "key2"}, stillMissingKeys) 1340 require.Empty(t, revisions) 1341 1342 // scenario 2: key1 is available in the cache 1343 require.NoError(t, vdbEnv.cache.putState(chainID, "ns1", "key1", &CacheValue{AdditionalInfo: []byte("rev1")})) 1344 revisions = make(map[string]string) 1345 stillMissingKeys, err = db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions) 1346 require.NoError(t, err) 1347 require.Equal(t, []string{"key2"}, stillMissingKeys) 1348 require.Equal(t, "rev1", revisions["key1"]) 1349 1350 // scenario 3: both key1 and key2 are available in the cache 1351 require.NoError(t, vdbEnv.cache.putState(chainID, "ns1", "key2", &CacheValue{AdditionalInfo: []byte("rev2")})) 1352 revisions = make(map[string]string) 1353 stillMissingKeys, err = db.(*VersionedDB).addMissingRevisionsFromCache("ns1", []string{"key1", "key2"}, revisions) 1354 require.NoError(t, err) 1355 require.Empty(t, stillMissingKeys) 1356 require.Equal(t, "rev1", revisions["key1"]) 1357 require.Equal(t, "rev2", revisions["key2"]) 1358 } 1359 1360 func TestChannelMetadata(t *testing.T) { 1361 vdbEnv.init(t, sysNamespaces) 1362 defer vdbEnv.cleanup() 1363 channelName := "testchannelmetadata" 1364 1365 db, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1366 require.NoError(t, err) 1367 vdb := db.(*VersionedDB) 1368 expectedChannelMetadata := &channelMetadata{ 1369 ChannelName: channelName, 1370 NamespaceDBsInfo: make(map[string]*namespaceDBInfo), 1371 } 1372 savedChannelMetadata, err := vdb.readChannelMetadata() 1373 require.NoError(t, err) 1374 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1375 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1376 1377 // call getNamespaceDBHandle for new dbs, verify that new db names are added to dbMetadataMapping 1378 namepsaces := make([]string, 10) 1379 for i := 0; i < 10; i++ { 1380 ns := fmt.Sprintf("nsname_%d", i) 1381 _, err := vdb.getNamespaceDBHandle(ns) 1382 require.NoError(t, err) 1383 namepsaces[i] = ns 1384 expectedChannelMetadata.NamespaceDBsInfo[ns] = &namespaceDBInfo{ 1385 Namespace: ns, 1386 DBName: constructNamespaceDBName(channelName, ns), 1387 } 1388 } 1389 1390 savedChannelMetadata, err = vdb.readChannelMetadata() 1391 require.NoError(t, err) 1392 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1393 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1394 1395 // call getNamespaceDBHandle for existing dbs, verify that no new db names are added to dbMetadataMapping 1396 for _, ns := range namepsaces { 1397 _, err := vdb.getNamespaceDBHandle(ns) 1398 require.NoError(t, err) 1399 } 1400 1401 savedChannelMetadata, err = vdb.readChannelMetadata() 1402 require.NoError(t, err) 1403 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1404 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1405 } 1406 1407 func TestChannelMetadata_NegativeTests(t *testing.T) { 1408 vdbEnv.init(t, sysNamespaces) 1409 defer vdbEnv.cleanup() 1410 1411 channelName := "testchannelmetadata-errorpropagation" 1412 origCouchAddress := vdbEnv.config.Address 1413 vdbEnv.config.MaxRetries = 1 1414 vdbEnv.config.MaxRetriesOnStartup = 1 1415 vdbEnv.config.RequestTimeout = 1 * time.Second 1416 1417 // simulate db connection error by setting an invalid address before GetDBHandle, verify error is propagated 1418 vdbEnv.config.Address = "127.0.0.1:1" 1419 expectedErrMsg := fmt.Sprintf("http error calling couchdb: Get \"http://%s/testchannelmetadata-errorpropagation_\": dial tcp %s: connect: connection refused", 1420 vdbEnv.config.Address, vdbEnv.config.Address) 1421 _, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1422 require.EqualError(t, err, expectedErrMsg) 1423 vdbEnv.config.Address = origCouchAddress 1424 1425 // simulate db connection error by setting an invalid address before getNamespaceDBHandle, verify error is propagated 1426 db, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1427 require.NoError(t, err) 1428 vdb := db.(*VersionedDB) 1429 vdbEnv.config.Address = "127.0.0.1:1" 1430 expectedErrMsg = fmt.Sprintf("http error calling couchdb: Put \"http://%s/testchannelmetadata-errorpropagation_/channel_metadata\": dial tcp %s: connect: connection refused", 1431 vdbEnv.config.Address, vdbEnv.config.Address) 1432 _, err = vdb.getNamespaceDBHandle("testnamepsace1") 1433 require.EqualError(t, err, expectedErrMsg) 1434 vdb.couchInstance.conf.Address = origCouchAddress 1435 1436 // call createCouchDatabase to simulate peer crashes after metadataDB is created but before channelMetadata is updated 1437 // then call DBProvider.GetDBHandle and verify channelMetadata is correctly generated 1438 channelName = "testchannelmetadata-simulatefailure-in-between" 1439 couchInstance, err := createCouchInstance(vdbEnv.config, &disabled.Provider{}) 1440 require.NoError(t, err) 1441 metadatadbName := constructMetadataDBName(channelName) 1442 metadataDB, err := createCouchDatabase(couchInstance, metadatadbName) 1443 require.NoError(t, err) 1444 vdb = &VersionedDB{ 1445 metadataDB: metadataDB, 1446 } 1447 savedChannelMetadata, err := vdb.readChannelMetadata() 1448 require.NoError(t, err) 1449 require.Nil(t, savedChannelMetadata) 1450 1451 db, err = vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1452 require.NoError(t, err) 1453 vdb = db.(*VersionedDB) 1454 expectedChannelMetadata := &channelMetadata{ 1455 ChannelName: channelName, 1456 NamespaceDBsInfo: make(map[string]*namespaceDBInfo), 1457 } 1458 savedChannelMetadata, err = vdb.readChannelMetadata() 1459 require.NoError(t, err) 1460 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1461 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1462 1463 // call writeChannelMetadata to simulate peer crashes after channelMetada is saved but before namespace DB is created 1464 // then call vdb.getNamespaceDBHandle and verify namespaceDB is created and channelMetadata is correct 1465 namespace := "testnamepsace2" 1466 namespaceDBName := constructNamespaceDBName(channelName, namespace) 1467 vdb.channelMetadata.NamespaceDBsInfo[namespace] = &namespaceDBInfo{Namespace: namespace, DBName: namespaceDBName} 1468 err = vdb.writeChannelMetadata() 1469 require.NoError(t, err) 1470 expectedChannelMetadata.NamespaceDBsInfo = map[string]*namespaceDBInfo{ 1471 namespace: {Namespace: namespace, DBName: namespaceDBName}, 1472 } 1473 savedChannelMetadata, err = vdb.readChannelMetadata() 1474 require.NoError(t, err) 1475 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1476 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1477 1478 _, err = vdb.getNamespaceDBHandle(namespace) 1479 require.NoError(t, err) 1480 savedChannelMetadata, err = vdb.readChannelMetadata() 1481 require.NoError(t, err) 1482 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1483 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1484 } 1485 1486 func TestInitChannelMetadta(t *testing.T) { 1487 vdbEnv.init(t, sysNamespaces) 1488 defer vdbEnv.cleanup() 1489 channelName1 := "testinithannelmetadata" 1490 channelName2 := "testinithannelmetadata_anotherchannel" 1491 1492 // create versioned DBs for channelName1 and channelName2 1493 db, err := vdbEnv.DBProvider.GetDBHandle(channelName1, nil) 1494 require.NoError(t, err) 1495 vdb := db.(*VersionedDB) 1496 db2, err := vdbEnv.DBProvider.GetDBHandle(channelName2, nil) 1497 require.NoError(t, err) 1498 vdb2 := db2.(*VersionedDB) 1499 1500 // prepare test data: 1501 // create dbs for channelName1: "ns1" and "ns3", which should match channelName1 namespaces 1502 // create dbs for channelName2: "ns2" and "ns4", which should not match any channelName1 namespaces 1503 _, err = vdb.getNamespaceDBHandle("ns1") 1504 require.NoError(t, err) 1505 _, err = vdb.getNamespaceDBHandle("ns3") 1506 require.NoError(t, err) 1507 _, err = vdb2.getNamespaceDBHandle("ns2") 1508 require.NoError(t, err) 1509 _, err = vdb2.getNamespaceDBHandle("ns4") 1510 require.NoError(t, err) 1511 1512 namespaces := []string{"ns1", "ns2", "ns3", "ns4"} 1513 fakeNsProvider := &mock.NamespaceProvider{} 1514 fakeNsProvider.PossibleNamespacesReturns(namespaces, nil) 1515 expectedDBsInfo := map[string]*namespaceDBInfo{ 1516 "ns1": {Namespace: "ns1", DBName: constructNamespaceDBName(channelName1, "ns1")}, 1517 "ns3": {Namespace: "ns3", DBName: constructNamespaceDBName(channelName1, "ns3")}, 1518 } 1519 expectedChannelMetadata := &channelMetadata{ 1520 ChannelName: channelName1, 1521 NamespaceDBsInfo: expectedDBsInfo, 1522 } 1523 1524 // test an existing DB with channelMetadata, namespace provider should not be called 1525 require.NoError(t, vdb.initChannelMetadata(false, fakeNsProvider)) 1526 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1527 require.Equal(t, 0, fakeNsProvider.PossibleNamespacesCallCount()) 1528 1529 // test an existing DB with no channelMetadata by deleting channelMetadata, namespace provider should be called 1530 require.NoError(t, vdb.metadataDB.deleteDoc(channelMetadataDocID, "")) 1531 require.NoError(t, vdb.initChannelMetadata(false, fakeNsProvider)) 1532 require.Equal(t, expectedChannelMetadata, vdb.channelMetadata) 1533 require.Equal(t, 1, fakeNsProvider.PossibleNamespacesCallCount()) 1534 savedChannelMetadata, err := vdb.readChannelMetadata() 1535 require.NoError(t, err) 1536 require.Equal(t, expectedChannelMetadata, savedChannelMetadata) 1537 1538 // test namespaceProvider error 1539 fakeNsProvider.PossibleNamespacesReturns(nil, errors.New("fake-namespaceprivder-error")) 1540 require.NoError(t, vdb.metadataDB.deleteDoc(channelMetadataDocID, "")) 1541 err = vdb.initChannelMetadata(false, fakeNsProvider) 1542 require.EqualError(t, err, "fake-namespaceprivder-error") 1543 1544 // test db error 1545 origCouchAddress := vdbEnv.config.Address 1546 vdbEnv.config.Address = "127.0.0.1:1" 1547 vdbEnv.config.MaxRetries = 1 1548 vdbEnv.config.MaxRetriesOnStartup = 1 1549 expectedErrMsg := fmt.Sprintf("http error calling couchdb: Get \"http://%s/testinithannelmetadata_/channel_metadata?attachments=true\": dial tcp %s: connect: connection refused", 1550 vdbEnv.config.Address, vdbEnv.config.Address) 1551 vdb.channelMetadata = nil 1552 err = vdb.initChannelMetadata(false, fakeNsProvider) 1553 require.EqualError(t, err, expectedErrMsg) 1554 vdbEnv.config.Address = origCouchAddress 1555 } 1556 1557 func TestRangeQueryWithInternalLimitAndPageSize(t *testing.T) { 1558 // generateSampleData returns a slice of KVs. The returned value contains 12 KVs for a namespace ns1 1559 generateSampleData := func() []*statedb.VersionedKV { 1560 sampleData := []*statedb.VersionedKV{} 1561 ver := version.NewHeight(1, 1) 1562 sampleKV := &statedb.VersionedKV{ 1563 CompositeKey: &statedb.CompositeKey{Namespace: "ns1", Key: string('\u0000')}, 1564 VersionedValue: &statedb.VersionedValue{Value: []byte("v0"), Version: ver, Metadata: []byte("m0")}, 1565 } 1566 sampleData = append(sampleData, sampleKV) 1567 for i := 0; i < 10; i++ { 1568 sampleKV = &statedb.VersionedKV{ 1569 CompositeKey: &statedb.CompositeKey{ 1570 Namespace: "ns1", 1571 Key: fmt.Sprintf("key-%d", i), 1572 }, 1573 VersionedValue: &statedb.VersionedValue{ 1574 Value: []byte(fmt.Sprintf("value-for-key-%d-for-ns1", i)), 1575 Version: ver, 1576 Metadata: []byte(fmt.Sprintf("metadata-for-key-%d-for-ns1", i)), 1577 }, 1578 } 1579 sampleData = append(sampleData, sampleKV) 1580 } 1581 sampleKV = &statedb.VersionedKV{ 1582 CompositeKey: &statedb.CompositeKey{ 1583 Namespace: "ns1", 1584 Key: string(utf8.MaxRune), 1585 }, 1586 VersionedValue: &statedb.VersionedValue{ 1587 Value: []byte("v1"), 1588 Version: ver, 1589 Metadata: []byte("m1"), 1590 }, 1591 } 1592 sampleData = append(sampleData, sampleKV) 1593 return sampleData 1594 } 1595 1596 vdbEnv.init(t, nil) 1597 defer vdbEnv.cleanup() 1598 channelName := "ch1" 1599 vdb, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1600 require.NoError(t, err) 1601 db := vdb.(*VersionedDB) 1602 1603 sampleData := generateSampleData() 1604 batch := statedb.NewUpdateBatch() 1605 for _, d := range sampleData { 1606 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1607 } 1608 require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(1, 1))) 1609 1610 defaultLimit := vdbEnv.config.InternalQueryLimit 1611 1612 // Scenario 1: We try to fetch either 11 records or all 12 records. We pass various internalQueryLimits. 1613 // key utf8.MaxRune would not be included as inclusive_end is always set to false 1614 testRangeQueryWithInternalLimit(t, "ns1", db, 2, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1615 testRangeQueryWithInternalLimit(t, "ns1", db, 5, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1616 testRangeQueryWithInternalLimit(t, "ns1", db, 2, string('\u0000'), "", sampleData) 1617 testRangeQueryWithInternalLimit(t, "ns1", db, 5, string('\u0000'), "", sampleData) 1618 testRangeQueryWithInternalLimit(t, "ns1", db, 2, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1619 testRangeQueryWithInternalLimit(t, "ns1", db, 5, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1620 testRangeQueryWithInternalLimit(t, "ns1", db, 2, "", "", sampleData) 1621 testRangeQueryWithInternalLimit(t, "ns1", db, 5, "", "", sampleData) 1622 1623 // Scenario 2: We try to fetch either 11 records or all 12 records using pagination. We pass various page sizes while 1624 // keeping the internalQueryLimit as the default one, i.e., 1000. 1625 vdbEnv.config.InternalQueryLimit = defaultLimit 1626 testRangeQueryWithPageSize(t, "ns1", db, 2, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1627 testRangeQueryWithPageSize(t, "ns1", db, 15, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1628 testRangeQueryWithPageSize(t, "ns1", db, 2, string('\u0000'), "", sampleData) 1629 testRangeQueryWithPageSize(t, "ns1", db, 15, string('\u0000'), "", sampleData) 1630 testRangeQueryWithPageSize(t, "ns1", db, 2, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1631 testRangeQueryWithPageSize(t, "ns1", db, 15, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1632 testRangeQueryWithPageSize(t, "ns1", db, 2, "", "", sampleData) 1633 testRangeQueryWithPageSize(t, "ns1", db, 15, "", "", sampleData) 1634 1635 // Scenario 3: We try to fetch either 11 records or all 12 records using pagination. We pass various page sizes while 1636 // keeping the internalQueryLimit to 1. 1637 vdbEnv.config.InternalQueryLimit = 1 1638 testRangeQueryWithPageSize(t, "ns1", db, 2, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1639 testRangeQueryWithPageSize(t, "ns1", db, 15, string('\u0000'), string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1640 testRangeQueryWithPageSize(t, "ns1", db, 2, string('\u0000'), "", sampleData) 1641 testRangeQueryWithPageSize(t, "ns1", db, 15, string('\u0000'), "", sampleData) 1642 testRangeQueryWithPageSize(t, "ns1", db, 2, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1643 testRangeQueryWithPageSize(t, "ns1", db, 15, "", string(utf8.MaxRune), sampleData[:len(sampleData)-1]) 1644 testRangeQueryWithPageSize(t, "ns1", db, 2, "", "", sampleData) 1645 testRangeQueryWithPageSize(t, "ns1", db, 15, "", "", sampleData) 1646 } 1647 1648 func testRangeQueryWithInternalLimit( 1649 t *testing.T, 1650 ns string, 1651 db *VersionedDB, 1652 limit int, 1653 startKey, endKey string, 1654 expectedResults []*statedb.VersionedKV, 1655 ) { 1656 vdbEnv.config.InternalQueryLimit = limit 1657 require.Equal(t, int32(limit), db.couchInstance.internalQueryLimit()) 1658 itr, err := db.GetStateRangeScanIterator(ns, startKey, endKey) 1659 require.NoError(t, err) 1660 require.Equal(t, int32(limit), itr.(*queryScanner).queryDefinition.internalQueryLimit) 1661 results := []*statedb.VersionedKV{} 1662 for { 1663 result, err := itr.Next() 1664 require.NoError(t, err) 1665 if result == nil { 1666 itr.Close() 1667 break 1668 } 1669 results = append(results, result) 1670 } 1671 require.Equal(t, expectedResults, results) 1672 } 1673 1674 func testRangeQueryWithPageSize( 1675 t *testing.T, 1676 ns string, 1677 db *VersionedDB, 1678 pageSize int, 1679 startKey, endKey string, 1680 expectedResults []*statedb.VersionedKV, 1681 ) { 1682 itr, err := db.GetStateRangeScanIteratorWithPagination(ns, startKey, endKey, int32(pageSize)) 1683 require.NoError(t, err) 1684 results := []*statedb.VersionedKV{} 1685 for { 1686 result, err := itr.Next() 1687 require.NoError(t, err) 1688 if result != nil { 1689 results = append(results, result) 1690 continue 1691 } 1692 nextStartKey := itr.GetBookmarkAndClose() 1693 if nextStartKey == endKey { 1694 break 1695 } 1696 itr, err = db.GetStateRangeScanIteratorWithPagination(ns, nextStartKey, endKey, int32(pageSize)) 1697 require.NoError(t, err) 1698 continue 1699 } 1700 require.Equal(t, expectedResults, results) 1701 } 1702 1703 func TestDataExportImport(t *testing.T) { 1704 t.Run("export-import", func(t *testing.T) { 1705 vdbEnv.init(t, nil) 1706 defer vdbEnv.cleanup() 1707 1708 for _, size := range []int{10, 2 * 1024 * 1024} { 1709 maxDataImportBatchMemorySize = size 1710 commontests.TestDataExportImport(t, vdbEnv.DBProvider) 1711 } 1712 }) 1713 1714 t.Run("nil-iterator", func(t *testing.T) { 1715 vdbEnv.init(t, nil) 1716 defer vdbEnv.cleanup() 1717 1718 require.NoError( 1719 t, 1720 vdbEnv.DBProvider.ImportFromSnapshot("testdb", nil, nil), 1721 ) 1722 }) 1723 1724 t.Run("error-couchdb-connection", func(t *testing.T) { 1725 vdbEnv.init(t, nil) 1726 configBackup := *vdbEnv.config 1727 defer func() { 1728 vdbEnv.config = &configBackup 1729 vdbEnv.cleanup() 1730 }() 1731 1732 vdbEnv.config.MaxRetries = 1 1733 vdbEnv.config.MaxRetriesOnStartup = 1 1734 vdbEnv.config.RequestTimeout = 1 * time.Second 1735 vdbEnv.config.Address = "127.0.0.1:1" 1736 require.Contains( 1737 t, 1738 vdbEnv.DBProvider.ImportFromSnapshot("testdb", nil, nil).Error(), 1739 "error while creating the metadata database for channel testdb: http error calling couchdb", 1740 ) 1741 }) 1742 1743 t.Run("error-reading-from-iter", func(t *testing.T) { 1744 vdbEnv.init(t, nil) 1745 defer vdbEnv.cleanup() 1746 1747 itr := &dummyFullScanIter{ 1748 err: errors.New("error while reading from source"), 1749 } 1750 require.EqualError( 1751 t, 1752 vdbEnv.DBProvider.ImportFromSnapshot("testdb", nil, itr), 1753 "error while reading from source", 1754 ) 1755 }) 1756 1757 t.Run("error-creating-database", func(t *testing.T) { 1758 vdbEnv.init(t, nil) 1759 configBackup := *vdbEnv.config 1760 defer func() { 1761 vdbEnv.config = &configBackup 1762 vdbEnv.cleanup() 1763 }() 1764 1765 vdb, err := vdbEnv.DBProvider.GetDBHandle("testdb", nil) 1766 require.NoError(t, err) 1767 nsDB, err := vdb.(*VersionedDB).getNamespaceDBHandle("ns") 1768 require.NoError(t, err) 1769 1770 vdbEnv.config.MaxRetries = 1 1771 vdbEnv.config.MaxRetriesOnStartup = 1 1772 vdbEnv.config.RequestTimeout = 1 * time.Second 1773 vdbEnv.config.Address = "127.0.0.1:1" 1774 1775 // creating database for the first encountered namespace 1776 s := &snapshotImporter{ 1777 vdb: vdb.(*VersionedDB), 1778 itr: &dummyFullScanIter{ 1779 kv: &statedb.VersionedKV{ 1780 CompositeKey: &statedb.CompositeKey{ 1781 Namespace: "ns1", 1782 }, 1783 }, 1784 }, 1785 } 1786 require.Contains( 1787 t, 1788 s.importState().Error(), 1789 "error while creating database for the namespace ns1", 1790 ) 1791 1792 // creating database as next namespace 1793 // does not match the current namespace 1794 s = &snapshotImporter{ 1795 vdb: vdb.(*VersionedDB), 1796 itr: &dummyFullScanIter{ 1797 kv: &statedb.VersionedKV{ 1798 CompositeKey: &statedb.CompositeKey{ 1799 Namespace: "ns2", 1800 }, 1801 }, 1802 }, 1803 currentNsDB: nsDB, 1804 currentNs: "ns", 1805 } 1806 require.Contains( 1807 t, 1808 s.importState().Error(), 1809 "error while creating database for the namespace ns2", 1810 ) 1811 }) 1812 1813 t.Run("error-while-storing", func(t *testing.T) { 1814 vdbEnv.init(t, nil) 1815 configBackup := *vdbEnv.config 1816 defer func() { 1817 vdbEnv.config = &configBackup 1818 vdbEnv.cleanup() 1819 }() 1820 1821 vdb, err := vdbEnv.DBProvider.GetDBHandle("testdb", nil) 1822 require.NoError(t, err) 1823 ns1DB, err := vdb.(*VersionedDB).getNamespaceDBHandle("ns1") 1824 require.NoError(t, err) 1825 1826 vdbEnv.config.MaxRetries = 1 1827 vdbEnv.config.MaxRetriesOnStartup = 1 1828 vdbEnv.config.RequestTimeout = 1 * time.Second 1829 vdbEnv.config.Address = "127.0.0.1:1" 1830 1831 // same namespace but the pending doc limit reached 1832 s := &snapshotImporter{ 1833 vdb: vdb.(*VersionedDB), 1834 itr: &dummyFullScanIter{ 1835 kv: &statedb.VersionedKV{ 1836 CompositeKey: &statedb.CompositeKey{ 1837 Namespace: "ns1", 1838 }, 1839 VersionedValue: &statedb.VersionedValue{ 1840 Value: []byte("random"), 1841 Version: version.NewHeight(1, 1), 1842 }, 1843 }, 1844 }, 1845 currentNsDB: ns1DB, 1846 currentNs: "ns1", 1847 pendingDocsBatch: []*couchDoc{{}, {}}, 1848 batchMemorySize: 4 * 1024 * 1024, 1849 } 1850 require.Contains( 1851 t, 1852 s.importState().Error(), 1853 "error while storing 3 states associated with namespace ns1", 1854 ) 1855 1856 // next namespace does not match the current namespace 1857 s = &snapshotImporter{ 1858 vdb: vdb.(*VersionedDB), 1859 itr: &dummyFullScanIter{ 1860 kv: &statedb.VersionedKV{ 1861 CompositeKey: &statedb.CompositeKey{ 1862 Namespace: "ns2", 1863 }, 1864 VersionedValue: &statedb.VersionedValue{ 1865 Value: []byte("random"), 1866 Version: version.NewHeight(1, 1), 1867 }, 1868 }, 1869 }, 1870 currentNsDB: ns1DB, 1871 currentNs: "ns1", 1872 pendingDocsBatch: []*couchDoc{{}, {}}, 1873 } 1874 require.Contains( 1875 t, 1876 s.importState().Error(), 1877 "error while storing 2 states associated with namespace ns1", 1878 ) 1879 }) 1880 } 1881 1882 func TestFullScanIteratorDeterministicJSONOutput(t *testing.T) { 1883 generateSampleData := func(ns string, sortedJSON bool) []*statedb.VersionedKV { 1884 sampleData := []*statedb.VersionedKV{} 1885 ver := version.NewHeight(1, 1) 1886 for i := 0; i < 10; i++ { 1887 sampleKV := &statedb.VersionedKV{ 1888 CompositeKey: &statedb.CompositeKey{ 1889 Namespace: ns, 1890 Key: fmt.Sprintf("key-%d", i), 1891 }, 1892 VersionedValue: &statedb.VersionedValue{ 1893 Version: ver, 1894 Metadata: []byte(fmt.Sprintf("metadata-for-key-%d-for-ns1", i)), 1895 }, 1896 } 1897 if sortedJSON { 1898 sampleKV.Value = []byte(fmt.Sprintf(`{"a":0,"b":0,"c":%d}`, i)) 1899 } else { 1900 sampleKV.Value = []byte(fmt.Sprintf(`{"c":%d,"b":0,"a":0}`, i)) 1901 } 1902 sampleData = append(sampleData, sampleKV) 1903 } 1904 return sampleData 1905 } 1906 1907 vdbEnv.init(t, nil) 1908 defer vdbEnv.cleanup() 1909 channelName := "ch1" 1910 vdb, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1911 require.NoError(t, err) 1912 db := vdb.(*VersionedDB) 1913 1914 // creating and storing JSON value with sorted keys 1915 sampleDataWithSortedJSON := generateSampleData("ns1", true) 1916 batch := statedb.NewUpdateBatch() 1917 for _, d := range sampleDataWithSortedJSON { 1918 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1919 } 1920 require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(1, 1))) 1921 1922 retrieveOnlyNs1 := func(ns string) bool { 1923 return ns != "ns1" 1924 } 1925 dbItr, err := db.GetFullScanIterator(retrieveOnlyNs1) 1926 require.NoError(t, err) 1927 require.NotNil(t, dbItr) 1928 verifyFullScanIterator(t, dbItr, sampleDataWithSortedJSON) 1929 1930 // creating and storing JSON value with unsorted JSON-keys 1931 sampleDataWithUnsortedJSON := generateSampleData("ns2", false) 1932 batch = statedb.NewUpdateBatch() 1933 for _, d := range sampleDataWithUnsortedJSON { 1934 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1935 } 1936 require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(1, 1))) 1937 1938 retrieveOnlyNs2 := func(ns string) bool { 1939 return ns != "ns2" 1940 } 1941 sampleDataWithSortedJSON = generateSampleData("ns2", true) 1942 dbItr, err = db.GetFullScanIterator(retrieveOnlyNs2) 1943 require.NoError(t, err) 1944 require.NotNil(t, dbItr) 1945 verifyFullScanIterator(t, dbItr, sampleDataWithSortedJSON) 1946 } 1947 1948 func TestFullScanIteratorSkipInternalKeys(t *testing.T) { 1949 generateSampleData := func(ns string, keys []string) []*statedb.VersionedKV { 1950 sampleData := []*statedb.VersionedKV{} 1951 ver := version.NewHeight(1, 1) 1952 for i := 0; i < len(keys); i++ { 1953 sampleKV := &statedb.VersionedKV{ 1954 CompositeKey: &statedb.CompositeKey{ 1955 Namespace: ns, 1956 Key: keys[i], 1957 }, 1958 VersionedValue: &statedb.VersionedValue{ 1959 Value: []byte(fmt.Sprintf("value-for-%s-for-ns1", keys[i])), 1960 Version: ver, 1961 Metadata: []byte(fmt.Sprintf("metadata-for-%s-for-ns1", keys[i])), 1962 }, 1963 } 1964 sampleData = append(sampleData, sampleKV) 1965 } 1966 return sampleData 1967 } 1968 1969 vdbEnv.init(t, nil) 1970 defer vdbEnv.cleanup() 1971 channelName := "ch1" 1972 vdb, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 1973 require.NoError(t, err) 1974 db := vdb.(*VersionedDB) 1975 1976 keys := []string{channelMetadataDocID, "key-1", "key-2", "key-3", "key-4", "key-5", savepointDocID} 1977 sampleData := generateSampleData("ns1", keys) 1978 batch := statedb.NewUpdateBatch() 1979 for _, d := range sampleData { 1980 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1981 } 1982 require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(1, 1))) 1983 1984 retrieveOnlyNs1 := func(ns string) bool { 1985 return ns != "ns1" 1986 } 1987 dbItr, err := db.GetFullScanIterator(retrieveOnlyNs1) 1988 require.NoError(t, err) 1989 require.NotNil(t, dbItr) 1990 verifyFullScanIterator(t, dbItr, sampleData) 1991 1992 sampleData = generateSampleData("", keys) 1993 batch = statedb.NewUpdateBatch() 1994 for _, d := range sampleData { 1995 batch.PutValAndMetadata(d.Namespace, d.Key, d.Value, d.Metadata, d.Version) 1996 } 1997 require.NoError(t, db.ApplyUpdates(batch, version.NewHeight(1, 1))) 1998 1999 retrieveOnlyEmptyNs := func(ns string) bool { 2000 return ns != "" 2001 } 2002 // remove internal keys such as savepointDocID and channelMetadataDocID 2003 // as it is an empty namespace 2004 keys = []string{"key-1", "key-2", "key-3", "key-4", "key-5"} 2005 sampleData = generateSampleData("", keys) 2006 dbItr, err = db.GetFullScanIterator(retrieveOnlyEmptyNs) 2007 require.NoError(t, err) 2008 require.NotNil(t, dbItr) 2009 verifyFullScanIterator(t, dbItr, sampleData) 2010 } 2011 2012 func verifyFullScanIterator( 2013 t *testing.T, 2014 dbIter statedb.FullScanIterator, 2015 expectedResult []*statedb.VersionedKV, 2016 ) { 2017 results := []*statedb.VersionedKV{} 2018 for { 2019 kv, err := dbIter.Next() 2020 require.NoError(t, err) 2021 if kv == nil { 2022 break 2023 } 2024 require.NoError(t, err) 2025 results = append(results, kv) 2026 } 2027 require.Equal(t, expectedResult, results) 2028 } 2029 2030 func TestDrop(t *testing.T) { 2031 vdbEnv.init(t, nil) 2032 defer vdbEnv.cleanup() 2033 2034 checkDBsAfterDropFunc := func(channelName string) { 2035 appDBNames := RetrieveApplicationDBNames(t, vdbEnv.config) 2036 for _, dbName := range appDBNames { 2037 require.NotContains(t, dbName, channelName+"_") 2038 } 2039 } 2040 2041 commontests.TestDrop(t, vdbEnv.DBProvider, checkDBsAfterDropFunc) 2042 } 2043 2044 func TestDropErrorPath(t *testing.T) { 2045 vdbEnv.init(t, nil) 2046 defer vdbEnv.cleanup() 2047 channelName := "testdroperror" 2048 2049 _, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 2050 require.NoError(t, err) 2051 2052 vdbEnv.config.MaxRetries = 1 2053 vdbEnv.config.MaxRetriesOnStartup = 1 2054 vdbEnv.config.RequestTimeout = 1 * time.Second 2055 origAddress := vdbEnv.config.Address 2056 vdbEnv.config.Address = "127.0.0.1:1" 2057 err = vdbEnv.DBProvider.Drop(channelName) 2058 require.Contains(t, err.Error(), "connection refused") 2059 vdbEnv.config.Address = origAddress 2060 2061 vdbEnv.DBProvider.Close() 2062 require.EqualError(t, vdbEnv.DBProvider.Drop(channelName), "internal leveldb error while obtaining db iterator: leveldb: closed") 2063 } 2064 2065 func TestReadFromDBInvalidKey(t *testing.T) { 2066 vdbEnv.init(t, sysNamespaces) 2067 defer vdbEnv.cleanup() 2068 channelName := "test_getstate_invalidkey" 2069 db, err := vdbEnv.DBProvider.GetDBHandle(channelName, nil) 2070 require.NoError(t, err) 2071 vdb := db.(*VersionedDB) 2072 2073 testcase := []struct { 2074 key string 2075 expectedErrorMsg string 2076 }{ 2077 { 2078 key: string([]byte{0xff, 0xfe, 0xfd}), 2079 expectedErrorMsg: "invalid key [fffefd], must be a UTF-8 string", 2080 }, 2081 { 2082 key: "", 2083 expectedErrorMsg: "invalid key. Empty string is not supported as a key by couchdb", 2084 }, 2085 { 2086 key: "_key_starting_with_an_underscore", 2087 expectedErrorMsg: `invalid key [_key_starting_with_an_underscore], cannot begin with "_"`, 2088 }, 2089 } 2090 2091 for i, tc := range testcase { 2092 t.Run(fmt.Sprintf("testcase-%d", i), func(t *testing.T) { 2093 _, err = vdb.readFromDB("ns", tc.key) 2094 require.EqualError(t, err, tc.expectedErrorMsg) 2095 }) 2096 } 2097 } 2098 2099 type dummyFullScanIter struct { 2100 err error 2101 kv *statedb.VersionedKV 2102 } 2103 2104 func (d *dummyFullScanIter) Next() (*statedb.VersionedKV, error) { 2105 return d.kv, d.err 2106 } 2107 2108 func (d *dummyFullScanIter) Close() { 2109 }