From b5e3affa73227df3e6f2f9617ffa34a69ffccb2a Mon Sep 17 00:00:00 2001 From: Lucas Menendez Date: Wed, 30 Aug 2023 16:24:39 +0200 Subject: [PATCH 1/3] trying to fix 'database is locked' issue according to the mattn/go-sqlite3 docs: https://github.com/mattn/go-sqlite3/\#faq --- cmd/census3/main.go | 12 +++++++++--- db/db.go | 6 +++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/cmd/census3/main.go b/cmd/census3/main.go index 78ae3a9c..437253cd 100644 --- a/cmd/census3/main.go +++ b/cmd/census3/main.go @@ -28,19 +28,19 @@ func main() { flag.Parse() log.Init(*logLevel, "stdout", nil) - db, q, err := db.Init(*dataDir) + database, q, err := db.Init(*dataDir) if err != nil { log.Fatal(err) } // Start the holder scanner - hc, err := service.NewHoldersScanner(db, q, *url) + hc, err := service.NewHoldersScanner(database, q, *url) if err != nil { log.Fatal(err) } // Start the API - err = api.Init(db, q, api.Census3APIConf{ + err = api.Init(database, q, api.Census3APIConf{ Hostname: "0.0.0.0", Port: *port, DataDir: *dataDir, @@ -60,6 +60,12 @@ func main() { log.Warnf("received SIGTERM, exiting at %s", time.Now().Format(time.RFC850)) cancel() log.Infof("waiting for routines to end gracefully...") + // closing database + go func() { + if err := database.Close(); err != nil { + log.Fatal(err) + } + }() time.Sleep(5 * time.Second) os.Exit(0) } diff --git a/db/db.go b/db/db.go index a8cacc79..13072c61 100644 --- a/db/db.go +++ b/db/db.go @@ -23,10 +23,14 @@ func Init(dataDir string) (*sql.DB, *queries.Queries, error) { } } // open database file - database, err := sql.Open("sqlite3", dbFile) + fileURI := fmt.Sprintf("file:%s?cache=shared", dbFile) + database, err := sql.Open("sqlite3", fileURI) if err != nil { return nil, nil, fmt.Errorf("error opening database: %w", err) } + // trying to fix "database is locked" issue according to the official + // mattn/go-sqlite3 docs: https://github.com/mattn/go-sqlite3/#faq + database.SetMaxOpenConns(1) // get census3 goose migrations and setup for sqlite3 if err := goose.SetDialect("sqlite3"); err != nil { return nil, nil, fmt.Errorf("error setting up driver for sqlite: %w", err) From 887add18d38415eff99f088185bd79c69e450bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Men=C3=A9ndez?= Date: Wed, 6 Sep 2023 17:18:03 +0200 Subject: [PATCH 2/3] using fix from vocdoni-node indexer --- cmd/census3/main.go | 6 +-- db/db.go | 69 +++++++++++++++++++++++++++------- service/helper_test.go | 12 +++--- service/holder_scanner_test.go | 40 ++++++++++---------- service/holders_scanner.go | 25 ++++++------ 5 files changed, 97 insertions(+), 55 deletions(-) diff --git a/cmd/census3/main.go b/cmd/census3/main.go index 437253cd..facda306 100644 --- a/cmd/census3/main.go +++ b/cmd/census3/main.go @@ -28,19 +28,19 @@ func main() { flag.Parse() log.Init(*logLevel, "stdout", nil) - database, q, err := db.Init(*dataDir) + database, err := db.Init(*dataDir) if err != nil { log.Fatal(err) } // Start the holder scanner - hc, err := service.NewHoldersScanner(database, q, *url) + hc, err := service.NewHoldersScanner(database, *url) if err != nil { log.Fatal(err) } // Start the API - err = api.Init(database, q, api.Census3APIConf{ + err = api.Init(database, api.Census3APIConf{ Hostname: "0.0.0.0", Port: *port, DataDir: *dataDir, diff --git a/db/db.go b/db/db.go index 13072c61..e79d5049 100644 --- a/db/db.go +++ b/db/db.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "time" _ "github.com/mattn/go-sqlite3" "github.com/pressly/goose/v3" @@ -15,31 +16,73 @@ import ( //go:embed migrations/*.sql var migrationsFS embed.FS -func Init(dataDir string) (*sql.DB, *queries.Queries, error) { +// DB struct abstact a safe connection with the database using sqlc queries, +// sqlite as a database engine and go-sqlite3 as a driver. +type DB struct { + RW *sql.DB + RO *sql.DB + + QueriesRW *queries.Queries + QueriesRO *queries.Queries +} + +// Close function stops all internal connections to the database +func (db *DB) Close() error { + if err := db.RW.Close(); err != nil { + return err + } + return db.RO.Close() +} + +// Init function starts a datbase using the data path provided as argument. It +// opens two different connections, one for read only, and another for read and +// write, with different configurations, optimized for each use case. +func Init(dataDir string) (*DB, error) { dbFile := filepath.Join(dataDir, "census3.sql") if _, err := os.Stat(dbFile); os.IsNotExist(err) { if err := os.MkdirAll(dataDir, os.ModePerm); err != nil { - return nil, nil, fmt.Errorf("error creating a new database file: %w", err) + return nil, fmt.Errorf("error creating a new database file: %w", err) } } - // open database file - fileURI := fmt.Sprintf("file:%s?cache=shared", dbFile) - database, err := sql.Open("sqlite3", fileURI) + // sqlite doesn't support multiple concurrent writers. + // For that reason, rwDB is limited to one open connection. + // Per https://github.com/mattn/go-sqlite3/issues/1022#issuecomment-1067353980, + // we use WAL to allow multiple concurrent readers at the same time. + rwDB, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?mode=rwc&_journal_mode=wal&_txlock=immediate&_synchronous=normal", dbFile)) if err != nil { - return nil, nil, fmt.Errorf("error opening database: %w", err) + return nil, fmt.Errorf("error opening database: %w", err) } - // trying to fix "database is locked" issue according to the official - // mattn/go-sqlite3 docs: https://github.com/mattn/go-sqlite3/#faq - database.SetMaxOpenConns(1) + rwDB.SetMaxOpenConns(1) + rwDB.SetMaxIdleConns(2) + rwDB.SetConnMaxIdleTime(10 * time.Minute) + rwDB.SetConnMaxLifetime(time.Hour) + + roDB, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?mode=ro&_journal_mode=wal", dbFile)) + if err != nil { + return nil, fmt.Errorf("error opening database: %w", err) + } + // Increasing these numbers can allow for more queries to run concurrently, + // but it also increases the memory used by sqlite and our connection pool. + // Most read-only queries we run are quick enough, so a small number seems OK. + roDB.SetMaxOpenConns(10) + roDB.SetMaxIdleConns(20) + roDB.SetConnMaxIdleTime(5 * time.Minute) + roDB.SetConnMaxLifetime(time.Hour) + // get census3 goose migrations and setup for sqlite3 if err := goose.SetDialect("sqlite3"); err != nil { - return nil, nil, fmt.Errorf("error setting up driver for sqlite: %w", err) + return nil, fmt.Errorf("error setting up driver for sqlite: %w", err) } goose.SetBaseFS(migrationsFS) // perform goose up - if err := goose.Up(database, "migrations"); err != nil { - return nil, nil, fmt.Errorf("error during goose up: %w", err) + if err := goose.Up(rwDB, "migrations"); err != nil { + return nil, fmt.Errorf("error during goose up: %w", err) } // init sqlc - return database, queries.New(database), nil + return &DB{ + RW: rwDB, + RO: roDB, + QueriesRW: queries.New(rwDB), + QueriesRO: queries.New(roDB), + }, nil } diff --git a/service/helper_test.go b/service/helper_test.go index da391eb5..39c67329 100644 --- a/service/helper_test.go +++ b/service/helper_test.go @@ -43,23 +43,23 @@ var ( ) type TestDB struct { - dir string - db *sql.DB - queries *queries.Queries + dir string + db *db.DB } func StartTestDB(t *testing.T) *TestDB { c := qt.New(t) dir := t.TempDir() - db, q, err := db.Init(dir) + db, err := db.Init(dir) c.Assert(err, qt.IsNil) - return &TestDB{dir, db, q} + return &TestDB{dir, db} } func (testdb *TestDB) Close(t *testing.T) { c := qt.New(t) - c.Assert(testdb.db.Close(), qt.IsNil) + c.Assert(testdb.db.RW.Close(), qt.IsNil) + c.Assert(testdb.db.RO.Close(), qt.IsNil) c.Assert(os.RemoveAll(testdb.dir), qt.IsNil) } diff --git a/service/holder_scanner_test.go b/service/holder_scanner_test.go index b76944a9..5a4fa957 100644 --- a/service/holder_scanner_test.go +++ b/service/holder_scanner_test.go @@ -22,24 +22,24 @@ func TestNewHolderScanner(t *testing.T) { testdb := StartTestDB(t) defer testdb.Close(t) - hs, err := NewHoldersScanner(testdb.db, testdb.queries, web3uri) + hs, err := NewHoldersScanner(testdb.db, web3uri) c.Assert(err, qt.IsNil) c.Assert(hs.lastBlock, qt.Equals, uint64(0)) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - _, err = testdb.queries.CreateBlock(ctx, queries.CreateBlockParams{ + _, err = testdb.db.QueriesRW.CreateBlock(ctx, queries.CreateBlockParams{ ID: 1000, Timestamp: "test", RootHash: []byte("test"), }) c.Assert(err, qt.IsNil) - hs, err = NewHoldersScanner(testdb.db, testdb.queries, web3uri) + hs, err = NewHoldersScanner(testdb.db, web3uri) c.Assert(err, qt.IsNil) c.Assert(hs.lastBlock, qt.Equals, uint64(1000)) - _, err = NewHoldersScanner(nil, nil, web3uri) + _, err = NewHoldersScanner(nil, web3uri) c.Assert(err, qt.IsNotNil) } @@ -52,7 +52,7 @@ func TestHolderScannerStart(t *testing.T) { defer testdb.Close(t) twg.Add(1) - hs, err := NewHoldersScanner(testdb.db, testdb.queries, web3uri) + hs, err := NewHoldersScanner(testdb.db, web3uri) c.Assert(err, qt.IsNil) go func() { hs.Start(ctx) @@ -69,7 +69,7 @@ func Test_tokenAddresses(t *testing.T) { testdb := StartTestDB(t) defer testdb.Close(t) - hs, err := NewHoldersScanner(testdb.db, testdb.queries, web3uri) + hs, err := NewHoldersScanner(testdb.db, web3uri) c.Assert(err, qt.IsNil) res, err := hs.tokenAddresses() @@ -78,7 +78,7 @@ func Test_tokenAddresses(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - _, err = testdb.queries.CreateToken(ctx, testTokenParams("0x1", "test0", + _, err = testdb.db.QueriesRW.CreateToken(ctx, testTokenParams("0x1", "test0", "test0", MonkeysDecimals, 0, MonkeysTotalSupply.Uint64(), uint64(state.CONTRACT_TYPE_ERC20), false)) c.Assert(err, qt.IsNil) @@ -87,7 +87,7 @@ func Test_tokenAddresses(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(res[common.HexToAddress("0x1")], qt.IsFalse) - _, err = testdb.queries.CreateToken(ctx, testTokenParams("0x2", "test2", + _, err = testdb.db.QueriesRW.CreateToken(ctx, testTokenParams("0x2", "test2", "test3", MonkeysDecimals, 10, MonkeysTotalSupply.Uint64(), uint64(state.CONTRACT_TYPE_ERC20), false)) c.Assert(err, qt.IsNil) @@ -103,13 +103,13 @@ func Test_saveHolders(t *testing.T) { testdb := StartTestDB(t) defer testdb.Close(t) - hs, err := NewHoldersScanner(testdb.db, testdb.queries, "http://google.com") + hs, err := NewHoldersScanner(testdb.db, "http://google.com") c.Assert(err, qt.IsNil) th := new(state.TokenHolders).Init(MonkeysAddress, state.CONTRACT_TYPE_ERC20, MonkeysCreationBlock) // no registered token c.Assert(hs.saveHolders(th), qt.ErrorIs, ErrTokenNotExists) - _, err = testdb.queries.CreateToken(context.Background(), testTokenParams( + _, err = testdb.db.QueriesRW.CreateToken(context.Background(), testTokenParams( MonkeysAddress.String(), MonkeysName, MonkeysSymbol, MonkeysDecimals, MonkeysCreationBlock, MonkeysTotalSupply.Uint64(), uint64(state.CONTRACT_TYPE_ERC20), false)) @@ -124,13 +124,13 @@ func Test_saveHolders(t *testing.T) { // check wrong web3 c.Assert(hs.saveHolders(th), qt.IsNotNil) // check new block created - _, err = testdb.queries.BlockByID(context.Background(), int64(MonkeysCreationBlock)) + _, err = testdb.db.QueriesRW.BlockByID(context.Background(), int64(MonkeysCreationBlock)) c.Assert(err, qt.ErrorIs, sql.ErrNoRows) // check good web3 hs.web3 = web3uri c.Assert(hs.saveHolders(th), qt.IsNil) // check new holders - res, err := testdb.queries.TokenHolderByTokenIDAndHolderID(context.Background(), + res, err := testdb.db.QueriesRW.TokenHolderByTokenIDAndHolderID(context.Background(), queries.TokenHolderByTokenIDAndHolderIDParams{ TokenID: MonkeysAddress.Bytes(), HolderID: holderAddr.Bytes(), @@ -140,7 +140,7 @@ func Test_saveHolders(t *testing.T) { // check update holders th.Append(holderAddr, holderBalance) c.Assert(hs.saveHolders(th), qt.IsNil) - res, err = testdb.queries.TokenHolderByTokenIDAndHolderID(context.Background(), + res, err = testdb.db.QueriesRW.TokenHolderByTokenIDAndHolderID(context.Background(), queries.TokenHolderByTokenIDAndHolderIDParams{ TokenID: MonkeysAddress.Bytes(), HolderID: holderAddr.Bytes(), @@ -151,7 +151,7 @@ func Test_saveHolders(t *testing.T) { // check delete holders th.Append(holderAddr, big.NewInt(-24)) c.Assert(hs.saveHolders(th), qt.IsNil) - _, err = testdb.queries.TokenHolderByTokenIDAndHolderID(context.Background(), + _, err = testdb.db.QueriesRW.TokenHolderByTokenIDAndHolderID(context.Background(), queries.TokenHolderByTokenIDAndHolderIDParams{ TokenID: MonkeysAddress.Bytes(), HolderID: holderAddr.Bytes(), @@ -165,7 +165,7 @@ func Test_scanHolders(t *testing.T) { testdb := StartTestDB(t) defer testdb.Close(t) - hs, err := NewHoldersScanner(testdb.db, testdb.queries, web3uri) + hs, err := NewHoldersScanner(testdb.db, web3uri) c.Assert(err, qt.IsNil) // token does not exists @@ -174,7 +174,7 @@ func Test_scanHolders(t *testing.T) { _, err = hs.scanHolders(ctx1, MonkeysAddress) c.Assert(err, qt.IsNotNil) - _, err = testdb.queries.CreateToken(context.Background(), testTokenParams( + _, err = testdb.db.QueriesRW.CreateToken(context.Background(), testTokenParams( MonkeysAddress.String(), MonkeysName, MonkeysSymbol, MonkeysDecimals, MonkeysCreationBlock, 10, uint64(state.CONTRACT_TYPE_ERC20), false)) c.Assert(err, qt.IsNil) @@ -184,7 +184,7 @@ func Test_scanHolders(t *testing.T) { _, err = hs.scanHolders(ctx2, MonkeysAddress) c.Assert(err, qt.IsNil) - res, err := testdb.queries.TokenHoldersByTokenID(context.Background(), MonkeysAddress.Bytes()) + res, err := testdb.db.QueriesRW.TokenHoldersByTokenID(context.Background(), MonkeysAddress.Bytes()) c.Assert(err, qt.IsNil) for _, holder := range res { balance, ok := MonkeysHolders[common.BytesToAddress(holder.ID)] @@ -199,18 +199,18 @@ func Test_calcTokenCreationBlock(t *testing.T) { testdb := StartTestDB(t) defer testdb.Close(t) - hs, err := NewHoldersScanner(testdb.db, testdb.queries, web3uri) + hs, err := NewHoldersScanner(testdb.db, web3uri) c.Assert(err, qt.IsNil) c.Assert(hs.calcTokenCreationBlock(context.Background(), MonkeysAddress), qt.IsNotNil) - _, err = testdb.queries.CreateToken(context.Background(), testTokenParams( + _, err = testdb.db.QueriesRW.CreateToken(context.Background(), testTokenParams( MonkeysAddress.String(), MonkeysName, MonkeysSymbol, MonkeysDecimals, MonkeysCreationBlock, MonkeysTotalSupply.Uint64(), uint64(state.CONTRACT_TYPE_ERC20), false)) c.Assert(err, qt.IsNil) c.Assert(hs.calcTokenCreationBlock(context.Background(), MonkeysAddress), qt.IsNil) - token, err := testdb.queries.TokenByID(context.Background(), MonkeysAddress.Bytes()) + token, err := testdb.db.QueriesRW.TokenByID(context.Background(), MonkeysAddress.Bytes()) c.Assert(err, qt.IsNil) c.Assert(uint64(token.CreationBlock.Int32), qt.Equals, MonkeysCreationBlock) } diff --git a/service/holders_scanner.go b/service/holders_scanner.go index d62a41e6..fccfb22c 100644 --- a/service/holders_scanner.go +++ b/service/holders_scanner.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" _ "github.com/mattn/go-sqlite3" + "github.com/vocdoni/census3/db" queries "github.com/vocdoni/census3/db/sqlc" "github.com/vocdoni/census3/state" "go.vocdoni.io/dvote/log" @@ -31,16 +32,15 @@ type HoldersScanner struct { web3 string tokens map[common.Address]*state.TokenHolders mutex sync.RWMutex - db *sql.DB - sqlc *queries.Queries + db *db.DB lastBlock uint64 } // NewHoldersScanner function creates a new HolderScanner using the dataDir path // and the web3 endpoint URI provided. It sets up a sqlite3 database instance // and gets the number of last block scanned from it. -func NewHoldersScanner(db *sql.DB, q *queries.Queries, w3uri string) (*HoldersScanner, error) { - if db == nil || q == nil { +func NewHoldersScanner(db *db.DB, w3uri string) (*HoldersScanner, error) { + if db == nil { return nil, ErrNoDB } // create an empty scanner @@ -48,12 +48,11 @@ func NewHoldersScanner(db *sql.DB, q *queries.Queries, w3uri string) (*HoldersSc tokens: make(map[common.Address]*state.TokenHolders), web3: w3uri, db: db, - sqlc: q, } // get latest analyzed block ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - lastBlock, err := s.sqlc.LastBlock(ctx) + lastBlock, err := s.db.QueriesRO.LastBlock(ctx) if err == nil { s.lastBlock = uint64(lastBlock) } @@ -120,7 +119,7 @@ func (s *HoldersScanner) tokenAddresses() (map[common.Address]bool, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // get tokens from the database - tokens, err := s.sqlc.ListTokens(ctx) + tokens, err := s.db.QueriesRO.ListTokens(ctx) // if error raises and is no rows error return nil results, if it is not // return the error. if err != nil { @@ -149,14 +148,14 @@ func (s *HoldersScanner) saveHolders(th *state.TokenHolders) error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // begin a transaction for group sql queries - tx, err := s.db.BeginTx(ctx, nil) + tx, err := s.db.RW.BeginTx(ctx, nil) if err != nil { return err } defer func() { _ = tx.Rollback() }() - qtx := s.sqlc.WithTx(tx) + qtx := s.db.QueriesRW.WithTx(tx) if exists, err := qtx.ExistsToken(ctx, th.Address().Bytes()); err != nil { return fmt.Errorf("error checking if token exists: %w", err) } else if !exists { @@ -303,13 +302,13 @@ func (s *HoldersScanner) scanHolders(ctx context.Context, addr common.Address) ( if !ok { log.Infof("initializing contract %s", addr.Hex()) // get token information from the database - tokenInfo, err := s.sqlc.TokenByID(ctx, addr.Bytes()) + tokenInfo, err := s.db.QueriesRO.TokenByID(ctx, addr.Bytes()) if err != nil { return false, err } ttype := state.TokenType(tokenInfo.TypeID) tokenLastBlock := uint64(tokenInfo.CreationBlock.Int32) - if blockNumber, err := s.sqlc.LastBlockByTokenID(ctx, addr.Bytes()); err == nil { + if blockNumber, err := s.db.QueriesRO.LastBlockByTokenID(ctx, addr.Bytes()); err == nil { tokenLastBlock = uint64(blockNumber) } th = new(state.TokenHolders).Init(addr, ttype, tokenLastBlock) @@ -363,7 +362,7 @@ func (s *HoldersScanner) calcTokenCreationBlock(ctx context.Context, addr common ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() // get the token type - tokenInfo, err := s.sqlc.TokenByID(ctx, addr.Bytes()) + tokenInfo, err := s.db.QueriesRO.TokenByID(ctx, addr.Bytes()) if err != nil { return fmt.Errorf("error getting token from database: %w", err) } @@ -383,7 +382,7 @@ func (s *HoldersScanner) calcTokenCreationBlock(ctx context.Context, addr common return fmt.Errorf("error getting token creation block value: %w", err) } // save the creation block into the database - _, err = s.sqlc.UpdateTokenCreationBlock(ctx, + _, err = s.db.QueriesRW.UpdateTokenCreationBlock(ctx, queries.UpdateTokenCreationBlockParams{ ID: addr.Bytes(), CreationBlock: *dbCreationBlock, From 639800f68da6999d454972aee682d34ef757d359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Men=C3=A9ndez?= Date: Thu, 7 Sep 2023 19:04:58 +0200 Subject: [PATCH 3/3] comments fixes --- api/censuses.go | 13 +------------ service/holder_scanner_test.go | 6 +++--- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/api/censuses.go b/api/censuses.go index 404f568d..9bcdb037 100644 --- a/api/censuses.go +++ b/api/censuses.go @@ -39,18 +39,7 @@ func (capi *census3API) getCensus(msg *api.APIdata, ctx *httprouter.HTTPContext) } internalCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - // begin a transaction for group sql queries - tx, err := capi.db.RO.BeginTx(internalCtx, nil) - if err != nil { - return ErrCantGetCensus - } - defer func() { - if err := tx.Rollback(); err != nil { - log.Errorw(err, "holders transaction rollback failed") - } - }() - qtx := capi.db.QueriesRO.WithTx(tx) - currentCensus, err := qtx.CensusByID(internalCtx, int64(censusID)) + currentCensus, err := capi.db.QueriesRO.CensusByID(internalCtx, int64(censusID)) if err != nil { if errors.Is(err, sql.ErrNoRows) { return ErrNotFoundCensus diff --git a/service/holder_scanner_test.go b/service/holder_scanner_test.go index 1713f984..ae63e1da 100644 --- a/service/holder_scanner_test.go +++ b/service/holder_scanner_test.go @@ -136,7 +136,7 @@ func Test_saveHolders(t *testing.T) { // check web3 c.Assert(hs.saveHolders(th), qt.IsNil) // check new holders - res, err := testdb.db.QueriesRW.TokenHolderByTokenIDAndHolderID(context.Background(), + res, err := testdb.db.QueriesRO.TokenHolderByTokenIDAndHolderID(context.Background(), queries.TokenHolderByTokenIDAndHolderIDParams{ TokenID: MonkeysAddress.Bytes(), HolderID: holderAddr.Bytes(), @@ -146,7 +146,7 @@ func Test_saveHolders(t *testing.T) { // check update holders th.Append(holderAddr, holderBalance) c.Assert(hs.saveHolders(th), qt.IsNil) - res, err = testdb.db.QueriesRW.TokenHolderByTokenIDAndHolderID(context.Background(), + res, err = testdb.db.QueriesRO.TokenHolderByTokenIDAndHolderID(context.Background(), queries.TokenHolderByTokenIDAndHolderIDParams{ TokenID: MonkeysAddress.Bytes(), HolderID: holderAddr.Bytes(), @@ -157,7 +157,7 @@ func Test_saveHolders(t *testing.T) { // check delete holders th.Append(holderAddr, big.NewInt(-24)) c.Assert(hs.saveHolders(th), qt.IsNil) - _, err = testdb.db.QueriesRW.TokenHolderByTokenIDAndHolderID(context.Background(), + _, err = testdb.db.QueriesRO.TokenHolderByTokenIDAndHolderID(context.Background(), queries.TokenHolderByTokenIDAndHolderIDParams{ TokenID: MonkeysAddress.Bytes(), HolderID: holderAddr.Bytes(),