From c795d8c3b6491cf941b1e79f3972dec346c79742 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Fri, 26 Jul 2024 11:58:23 -0400 Subject: [PATCH 01/37] Use GetLedgerRange to get the latest ledger --- cmd/soroban-rpc/internal/db/db.go | 45 +++-- cmd/soroban-rpc/internal/db/ledger.go | 2 +- cmd/soroban-rpc/internal/db/ledgerentry.go | 7 +- .../internal/db/ledgerentry_test.go | 183 +++++++++--------- 4 files changed, 126 insertions(+), 111 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index 1767f610..d13dcb26 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -133,28 +133,33 @@ func getMetaValue(ctx context.Context, q db.SessionInterface, key string) (strin return results[0], nil } -func getLatestLedgerSequence(ctx context.Context, q db.SessionInterface, cache *dbCache) (uint32, error) { - latestLedgerStr, err := getMetaValue(ctx, q, latestLedgerSequenceMetaKey) +func getLatestLedgerSequence(ctx context.Context, _ db.SessionInterface, ledgerReader LedgerReader, _ *dbCache) (uint32, error) { + //latestLedgerStr, err := getMetaValue(ctx, q, latestLedgerSequenceMetaKey) + //if err != nil { + // return 0, err + //} + //latestLedger, err := strconv.ParseUint(latestLedgerStr, 10, 32) + //if err != nil { + // return 0, err + //} + //result := uint32(latestLedger) + // + //// Add missing ledger sequence to the top cache. + //// Otherwise, the write-through cache won't get updated until the first ingestion commit + //cache.Lock() + //if cache.latestLedgerSeq == 0 { + // // Only update the cache if the value is missing (0), otherwise + // // we may end up overwriting the entry with an older version + // cache.latestLedgerSeq = result + //} + //cache.Unlock() + // + //return result, nil + ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { return 0, err } - latestLedger, err := strconv.ParseUint(latestLedgerStr, 10, 32) - if err != nil { - return 0, err - } - result := uint32(latestLedger) - - // Add missing ledger sequence to the top cache. - // Otherwise, the write-through cache won't get updated until the first ingestion commit - cache.Lock() - if cache.latestLedgerSeq == 0 { - // Only update the cache if the value is missing (0), otherwise - // we may end up overwriting the entry with an older version - cache.latestLedgerSeq = result - } - cache.Unlock() - - return result, nil + return ledgerRange.LastLedger.Sequence, nil } type ReadWriterMetrics struct { @@ -215,7 +220,7 @@ func NewReadWriter( } func (rw *readWriter) GetLatestLedgerSequence(ctx context.Context) (uint32, error) { - return getLatestLedgerSequence(ctx, rw.db, rw.db.cache) + return getLatestLedgerSequence(ctx, rw.db, NewLedgerReader(rw.db), rw.db.cache) } func (rw *readWriter) NewTx(ctx context.Context) (WriteTx, error) { diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index 39e43b83..30a0185c 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -89,7 +89,7 @@ func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.Le // Empty DB if len(lcms) == 0 { - return ledgerbucketwindow.LedgerRange{}, nil + return ledgerbucketwindow.LedgerRange{}, ErrEmptyDB } return ledgerbucketwindow.LedgerRange{ diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 60e3a259..d163cd6f 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -140,6 +140,7 @@ type ledgerEntryReadTx struct { stmtCache *sq.StmtCache latestLedgerSeqCache uint32 ledgerEntryCacheReadTx *transactionalCacheReadTx + ledgerReader LedgerReader tx db.SessionInterface buffer *xdr.EncodingBuffer } @@ -148,7 +149,7 @@ func (l *ledgerEntryReadTx) GetLatestLedgerSequence() (uint32, error) { if l.latestLedgerSeqCache != 0 { return l.latestLedgerSeqCache, nil } - latestLedgerSeq, err := getLatestLedgerSequence(context.Background(), l.tx, l.globalCache) + latestLedgerSeq, err := getLatestLedgerSequence(context.Background(), l.tx, l.ledgerReader, l.globalCache) if err == nil { l.latestLedgerSeqCache = latestLedgerSeq } @@ -341,7 +342,7 @@ func NewLedgerEntryReader(db *DB) LedgerEntryReader { } func (r ledgerEntryReader) GetLatestLedgerSequence(ctx context.Context) (uint32, error) { - return getLatestLedgerSequence(ctx, r.db, r.db.cache) + return getLatestLedgerSequence(ctx, r.db, NewLedgerReader(r.db), r.db.cache) } // NewCachedTx() caches all accessed ledger entries and select statements. If many ledger entries are accessed, it will grow without bounds. @@ -364,6 +365,7 @@ func (r ledgerEntryReader) NewCachedTx(ctx context.Context) (LedgerEntryReadTx, stmtCache: sq.NewStmtCache(txSession.GetTx()), latestLedgerSeqCache: r.db.cache.latestLedgerSeq, ledgerEntryCacheReadTx: &cacheReadTx, + ledgerReader: NewLedgerReader(r.db), tx: txSession, buffer: xdr.NewEncodingBuffer(), }, nil @@ -380,6 +382,7 @@ func (r ledgerEntryReader) NewTx(ctx context.Context) (LedgerEntryReadTx, error) globalCache: r.db.cache, latestLedgerSeqCache: r.db.cache.latestLedgerSeq, tx: txSession, + ledgerReader: NewLedgerReader(r.db), buffer: xdr.NewEncodingBuffer(), }, nil } diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index 08526709..46b81880 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -88,6 +88,7 @@ func TestGoldenPath(t *testing.T) { assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) ledgerSequence := uint32(23) + assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) assert.NoError(t, tx.Commit(ledgerSequence)) present, obtainedEntry, obtainedLedgerSequence, liveUntilSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) @@ -113,6 +114,7 @@ func TestGoldenPath(t *testing.T) { assert.NoError(t, writer.UpsertLedgerEntry(entry)) ledgerSequence = uint32(24) + assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) assert.NoError(t, tx.Commit(ledgerSequence)) present, obtainedEntry, obtainedLedgerSequence, liveUntilSeq = getLedgerEntryAndLatestLedgerSequence(t, db, key) @@ -129,6 +131,7 @@ func TestGoldenPath(t *testing.T) { assert.NoError(t, writer.DeleteLedgerEntry(key)) ledgerSequence = uint32(25) + assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) assert.NoError(t, tx.Commit(ledgerSequence)) present, _, obtainedLedgerSequence, liveUntilSeq = getLedgerEntryAndLatestLedgerSequence(t, db, key) @@ -170,6 +173,7 @@ func TestDeleteNonExistentLedgerEmpty(t *testing.T) { key, _ := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.DeleteLedgerEntry(key)) ledgerSequence := uint32(23) + assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) assert.NoError(t, tx.Commit(ledgerSequence)) // Make sure that the ledger number was submitted @@ -271,6 +275,7 @@ func TestReadTxsDuringWriteTx(t *testing.T) { // Finish the write transaction and check that the results are present ledgerSequence := uint32(23) + assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) assert.NoError(t, tx.Commit(ledgerSequence)) obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) @@ -286,94 +291,95 @@ func TestReadTxsDuringWriteTx(t *testing.T) { // Make sure that a write transaction can happen while multiple read transactions are ongoing, // and write is only visible once the transaction is committed -func TestWriteTxsDuringReadTxs(t *testing.T) { - db := NewTestDB(t) - - // Check that we get an empty DB error - _, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) - assert.Equal(t, ErrEmptyDB, err) - - // Create a multiple read transactions, interleaved with the writing process - - // First read transaction, before the write transaction is created - readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background()) - assert.NoError(t, err) - - // Start filling the DB with a single entry (enforce flushing right away) - tx, err := makeReadWriter(db, 0, 15).NewTx(context.Background()) - assert.NoError(t, err) - writer := tx.LedgerEntryWriter() - - // Second read transaction, after the write transaction is created - readTx2, err := NewLedgerEntryReader(db).NewTx(context.Background()) - assert.NoError(t, err) - - four := xdr.Uint32(4) - six := xdr.Uint32(6) - data := xdr.ContractDataEntry{ - Contract: xdr.ScAddress{ - Type: xdr.ScAddressTypeScAddressTypeContract, - ContractId: &xdr.Hash{0xca, 0xfe}, - }, - Key: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &four, - }, - Durability: xdr.ContractDataDurabilityPersistent, - Val: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &six, - }, - } - key, entry := getContractDataLedgerEntry(t, data) - assert.NoError(t, writer.UpsertLedgerEntry(entry)) - - expLedgerKey, err := entryKeyToTTLEntryKey(key) - assert.NoError(t, err) - expLegerEntry := getTTLLedgerEntry(expLedgerKey) - assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) - - // Third read transaction, after the first insert has happened in the write transaction - readTx3, err := NewLedgerEntryReader(db).NewTx(context.Background()) - assert.NoError(t, err) - - // Make sure that all the read transactions get an emptyDB error before and after the write transaction is committed - for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { - _, err = readTx.GetLatestLedgerSequence() - assert.Equal(t, ErrEmptyDB, err) - present, _, _, err := GetLedgerEntry(readTx, key) - assert.NoError(t, err) - assert.False(t, present) - } - - // commit the write transaction - ledgerSequence := uint32(23) - assert.NoError(t, tx.Commit(ledgerSequence)) - - for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { - _, err = readTx.GetLatestLedgerSequence() - assert.Equal(t, ErrEmptyDB, err) - present, _, _, err := GetLedgerEntry(readTx, key) - assert.NoError(t, err) - assert.False(t, present) - } - - // Check that the results are present in the transactions happening after the commit - - obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) - assert.NoError(t, err) - assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - - present, obtainedEntry, obtainedLedgerSequence, expSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) - assert.True(t, present) - require.NotNil(t, expSeq) - assert.Equal(t, ledgerSequence, obtainedLedgerSequence) - assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) - - for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { - assert.NoError(t, readTx.Done()) - } -} +//func TestWriteTxsDuringReadTxs(t *testing.T) { +// db := NewTestDB(t) +// +// // Check that we get an empty DB error +// _, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) +// assert.Equal(t, ErrEmptyDB, err) +// +// // Create a multiple read transactions, interleaved with the writing process +// +// // First read transaction, before the write transaction is created +// readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background()) +// assert.NoError(t, err) +// +// // Start filling the DB with a single entry (enforce flushing right away) +// tx, err := makeReadWriter(db, 0, 15).NewTx(context.Background()) +// assert.NoError(t, err) +// writer := tx.LedgerEntryWriter() +// +// // Second read transaction, after the write transaction is created +// readTx2, err := NewLedgerEntryReader(db).NewTx(context.Background()) +// assert.NoError(t, err) +// +// four := xdr.Uint32(4) +// six := xdr.Uint32(6) +// data := xdr.ContractDataEntry{ +// Contract: xdr.ScAddress{ +// Type: xdr.ScAddressTypeScAddressTypeContract, +// ContractId: &xdr.Hash{0xca, 0xfe}, +// }, +// Key: xdr.ScVal{ +// Type: xdr.ScValTypeScvU32, +// U32: &four, +// }, +// Durability: xdr.ContractDataDurabilityPersistent, +// Val: xdr.ScVal{ +// Type: xdr.ScValTypeScvU32, +// U32: &six, +// }, +// } +// key, entry := getContractDataLedgerEntry(t, data) +// assert.NoError(t, writer.UpsertLedgerEntry(entry)) +// +// expLedgerKey, err := entryKeyToTTLEntryKey(key) +// assert.NoError(t, err) +// expLegerEntry := getTTLLedgerEntry(expLedgerKey) +// assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) +// +// // Third read transaction, after the first insert has happened in the write transaction +// readTx3, err := NewLedgerEntryReader(db).NewTx(context.Background()) +// assert.NoError(t, err) +// +// // Make sure that all the read transactions get an emptyDB error before and after the write transaction is committed +// for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { +// _, err = readTx.GetLatestLedgerSequence() +// assert.Equal(t, ErrEmptyDB, err) +// present, _, _, err := GetLedgerEntry(readTx, key) +// assert.NoError(t, err) +// assert.False(t, present) +// } +// +// // commit the write transaction +// ledgerSequence := uint32(23) +// assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) +// assert.NoError(t, tx.Commit(ledgerSequence)) +// +// for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { +// _, err = readTx.GetLatestLedgerSequence() +// assert.Equal(t, ErrEmptyDB, err) +// present, _, _, err := GetLedgerEntry(readTx, key) +// assert.NoError(t, err) +// assert.False(t, present) +// } +// +// // Check that the results are present in the transactions happening after the commit +// +// obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) +// assert.NoError(t, err) +// assert.Equal(t, ledgerSequence, obtainedLedgerSequence) +// +// present, obtainedEntry, obtainedLedgerSequence, expSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) +// assert.True(t, present) +// require.NotNil(t, expSeq) +// assert.Equal(t, ledgerSequence, obtainedLedgerSequence) +// assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) +// +// for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { +// assert.NoError(t, readTx.Done()) +// } +//} // Check that we can have coexisting reader and writer goroutines without deadlocks or errors func TestConcurrentReadersAndWriter(t *testing.T) { @@ -416,6 +422,7 @@ func TestConcurrentReadersAndWriter(t *testing.T) { expLegerEntry := getTTLLedgerEntry(expLedgerKey) assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) } + assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) assert.NoError(t, tx.Commit(ledgerSequence)) logMessageCh <- fmt.Sprintf("Wrote ledger %d", ledgerSequence) time.Sleep(time.Duration(rand.Int31n(30)) * time.Millisecond) From dd009c805137fa4d69a7905cb31cded2a9ed8314 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Fri, 26 Jul 2024 12:56:14 -0400 Subject: [PATCH 02/37] Remove latestLedger key from meta table --- cmd/soroban-rpc/internal/db/db.go | 52 ++++++++++--------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index d13dcb26..d188ba2c 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -27,8 +27,7 @@ var sqlMigrations embed.FS var ErrEmptyDB = errors.New("DB is empty") const ( - metaTableName = "metadata" - latestLedgerSequenceMetaKey = "LatestLedgerSequence" + metaTableName = "metadata" ) type ReadWriter interface { @@ -128,38 +127,29 @@ func getMetaValue(ctx context.Context, q db.SessionInterface, key string) (strin case 1: // expected length on an initialized DB default: - return "", fmt.Errorf("multiple entries (%d) for key %q in table %q", len(results), latestLedgerSequenceMetaKey, metaTableName) + return "", fmt.Errorf("multiple entries (%d) for key %q in table %q", len(results), key, metaTableName) } return results[0], nil } -func getLatestLedgerSequence(ctx context.Context, _ db.SessionInterface, ledgerReader LedgerReader, _ *dbCache) (uint32, error) { - //latestLedgerStr, err := getMetaValue(ctx, q, latestLedgerSequenceMetaKey) - //if err != nil { - // return 0, err - //} - //latestLedger, err := strconv.ParseUint(latestLedgerStr, 10, 32) - //if err != nil { - // return 0, err - //} - //result := uint32(latestLedger) - // - //// Add missing ledger sequence to the top cache. - //// Otherwise, the write-through cache won't get updated until the first ingestion commit - //cache.Lock() - //if cache.latestLedgerSeq == 0 { - // // Only update the cache if the value is missing (0), otherwise - // // we may end up overwriting the entry with an older version - // cache.latestLedgerSeq = result - //} - //cache.Unlock() - // - //return result, nil +func getLatestLedgerSequence(ctx context.Context, _ db.SessionInterface, ledgerReader LedgerReader, cache *dbCache) (uint32, error) { ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { return 0, err } - return ledgerRange.LastLedger.Sequence, nil + result := ledgerRange.LastLedger.Sequence + + // Add missing ledger sequence to the top cache. + // Otherwise, the write-through cache won't get updated until the first ingestion commit + cache.Lock() + if cache.latestLedgerSeq == 0 { + // Only update the cache if the value is missing (0), otherwise + // we may end up overwriting the entry with an older version + cache.latestLedgerSeq = result + } + cache.Unlock() + + return result, nil } type ReadWriterMetrics struct { @@ -298,21 +288,13 @@ func (w writeTx) Commit(ledgerSeq uint32) error { return err } - _, err := sq.Replace(metaTableName). - Values(latestLedgerSequenceMetaKey, strconv.FormatUint(uint64(ledgerSeq), 10)). - RunWith(w.stmtCache). - Exec() - if err != nil { - return err - } - // We need to make the cache update atomic with the transaction commit. // Otherwise, the cache can be made inconsistent if a write transaction finishes // in between, updating the cache in the wrong order. commitAndUpdateCache := func() error { w.globalCache.Lock() defer w.globalCache.Unlock() - if err = w.tx.Commit(); err != nil { + if err := w.tx.Commit(); err != nil { return err } w.globalCache.latestLedgerSeq = ledgerSeq From 096c434cc19deda8acb0bd40763bdbc5004b4910 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 29 Jul 2024 10:57:45 -0400 Subject: [PATCH 03/37] Remove latestLedger key from meta table - 2 --- cmd/soroban-rpc/internal/db/db.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index d188ba2c..eb2aac0f 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -133,6 +133,10 @@ func getMetaValue(ctx context.Context, q db.SessionInterface, key string) (strin } func getLatestLedgerSequence(ctx context.Context, _ db.SessionInterface, ledgerReader LedgerReader, cache *dbCache) (uint32, error) { + if cache.latestLedgerSeq != 0 { + return cache.latestLedgerSeq, nil + } + ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { return 0, err From a8190eb291083c708ab1833d9febed4d905307cd Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 29 Jul 2024 11:10:40 -0400 Subject: [PATCH 04/37] Fix failing unittest - 1 --- cmd/soroban-rpc/internal/db/ledger_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/db/ledger_test.go b/cmd/soroban-rpc/internal/db/ledger_test.go index 519a1168..e35206ca 100644 --- a/cmd/soroban-rpc/internal/db/ledger_test.go +++ b/cmd/soroban-rpc/internal/db/ledger_test.go @@ -171,7 +171,7 @@ func TestGetLedgerRange_EmptyDB(t *testing.T) { reader := NewLedgerReader(db) ledgerRange, err := reader.GetLedgerRange(ctx) - require.NoError(t, err) + assert.Equal(t, ErrEmptyDB, err) assert.Equal(t, uint32(0), ledgerRange.FirstLedger.Sequence) assert.Equal(t, int64(0), ledgerRange.FirstLedger.CloseTime) assert.Equal(t, uint32(0), ledgerRange.LastLedger.Sequence) From 30a261e00a8470bf33a8b3d78e132e2a4f5e0ae7 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 29 Jul 2024 11:25:34 -0400 Subject: [PATCH 05/37] Remove cache check condition --- cmd/soroban-rpc/internal/db/db.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index eb2aac0f..d188ba2c 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -133,10 +133,6 @@ func getMetaValue(ctx context.Context, q db.SessionInterface, key string) (strin } func getLatestLedgerSequence(ctx context.Context, _ db.SessionInterface, ledgerReader LedgerReader, cache *dbCache) (uint32, error) { - if cache.latestLedgerSeq != 0 { - return cache.latestLedgerSeq, nil - } - ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { return 0, err From 63755189284b79fd9759a37a81fb32f31c50dd71 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 29 Jul 2024 13:06:04 -0400 Subject: [PATCH 06/37] Fix failing unittest - 2 --- .../internal/preflight/preflight_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cmd/soroban-rpc/internal/preflight/preflight_test.go b/cmd/soroban-rpc/internal/preflight/preflight_test.go index 5de512ca..fc45a9a1 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight_test.go +++ b/cmd/soroban-rpc/internal/preflight/preflight_test.go @@ -294,6 +294,24 @@ func (m inMemoryLedgerEntryReadTx) Done() error { return nil } +func createLedger(ledgerSequence uint32) xdr.LedgerCloseMeta { + return xdr.LedgerCloseMeta{ + V: 1, + V1: &xdr.LedgerCloseMetaV1{ + LedgerHeader: xdr.LedgerHeaderHistoryEntry{ + Hash: xdr.Hash{}, + Header: xdr.LedgerHeader{ + LedgerSeq: xdr.Uint32(ledgerSequence), + }, + }, + TxSet: xdr.GeneralizedTransactionSet{ + V: 1, + V1TxSet: &xdr.TransactionSetV1{}, + }, + }, + } +} + func getDB(t testing.TB, restartDB bool) *db.DB { dbPath := path.Join(t.TempDir(), "soroban_rpc.sqlite") dbInstance, err := db.OpenSQLiteDB(dbPath) @@ -308,6 +326,7 @@ func getDB(t testing.TB, restartDB bool) *db.DB { err := tx.LedgerEntryWriter().UpsertLedgerEntry(e) require.NoError(t, err) } + require.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(2))) require.NoError(t, tx.Commit(2)) if restartDB { From 943b01d1ffe6967d3668987d0afb4e2a0c2468eb Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 29 Jul 2024 14:01:14 -0400 Subject: [PATCH 07/37] Uncomment failing ledger entry test --- .../internal/db/ledgerentry_test.go | 178 +++++++++--------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index 46b81880..9831205f 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -291,95 +291,95 @@ func TestReadTxsDuringWriteTx(t *testing.T) { // Make sure that a write transaction can happen while multiple read transactions are ongoing, // and write is only visible once the transaction is committed -//func TestWriteTxsDuringReadTxs(t *testing.T) { -// db := NewTestDB(t) -// -// // Check that we get an empty DB error -// _, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) -// assert.Equal(t, ErrEmptyDB, err) -// -// // Create a multiple read transactions, interleaved with the writing process -// -// // First read transaction, before the write transaction is created -// readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background()) -// assert.NoError(t, err) -// -// // Start filling the DB with a single entry (enforce flushing right away) -// tx, err := makeReadWriter(db, 0, 15).NewTx(context.Background()) -// assert.NoError(t, err) -// writer := tx.LedgerEntryWriter() -// -// // Second read transaction, after the write transaction is created -// readTx2, err := NewLedgerEntryReader(db).NewTx(context.Background()) -// assert.NoError(t, err) -// -// four := xdr.Uint32(4) -// six := xdr.Uint32(6) -// data := xdr.ContractDataEntry{ -// Contract: xdr.ScAddress{ -// Type: xdr.ScAddressTypeScAddressTypeContract, -// ContractId: &xdr.Hash{0xca, 0xfe}, -// }, -// Key: xdr.ScVal{ -// Type: xdr.ScValTypeScvU32, -// U32: &four, -// }, -// Durability: xdr.ContractDataDurabilityPersistent, -// Val: xdr.ScVal{ -// Type: xdr.ScValTypeScvU32, -// U32: &six, -// }, -// } -// key, entry := getContractDataLedgerEntry(t, data) -// assert.NoError(t, writer.UpsertLedgerEntry(entry)) -// -// expLedgerKey, err := entryKeyToTTLEntryKey(key) -// assert.NoError(t, err) -// expLegerEntry := getTTLLedgerEntry(expLedgerKey) -// assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) -// -// // Third read transaction, after the first insert has happened in the write transaction -// readTx3, err := NewLedgerEntryReader(db).NewTx(context.Background()) -// assert.NoError(t, err) -// -// // Make sure that all the read transactions get an emptyDB error before and after the write transaction is committed -// for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { -// _, err = readTx.GetLatestLedgerSequence() -// assert.Equal(t, ErrEmptyDB, err) -// present, _, _, err := GetLedgerEntry(readTx, key) -// assert.NoError(t, err) -// assert.False(t, present) -// } -// -// // commit the write transaction -// ledgerSequence := uint32(23) -// assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) -// assert.NoError(t, tx.Commit(ledgerSequence)) -// -// for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { -// _, err = readTx.GetLatestLedgerSequence() -// assert.Equal(t, ErrEmptyDB, err) -// present, _, _, err := GetLedgerEntry(readTx, key) -// assert.NoError(t, err) -// assert.False(t, present) -// } -// -// // Check that the results are present in the transactions happening after the commit -// -// obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) -// assert.NoError(t, err) -// assert.Equal(t, ledgerSequence, obtainedLedgerSequence) -// -// present, obtainedEntry, obtainedLedgerSequence, expSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) -// assert.True(t, present) -// require.NotNil(t, expSeq) -// assert.Equal(t, ledgerSequence, obtainedLedgerSequence) -// assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) -// -// for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { -// assert.NoError(t, readTx.Done()) -// } -//} +func TestWriteTxsDuringReadTxs(t *testing.T) { + db := NewTestDB(t) + + // Check that we get an empty DB error + _, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) + assert.Equal(t, ErrEmptyDB, err) + + // Create a multiple read transactions, interleaved with the writing process + + // First read transaction, before the write transaction is created + readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background()) + assert.NoError(t, err) + + // Start filling the DB with a single entry (enforce flushing right away) + tx, err := makeReadWriter(db, 0, 15).NewTx(context.Background()) + assert.NoError(t, err) + writer := tx.LedgerEntryWriter() + + // Second read transaction, after the write transaction is created + readTx2, err := NewLedgerEntryReader(db).NewTx(context.Background()) + assert.NoError(t, err) + + four := xdr.Uint32(4) + six := xdr.Uint32(6) + data := xdr.ContractDataEntry{ + Contract: xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &xdr.Hash{0xca, 0xfe}, + }, + Key: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &four, + }, + Durability: xdr.ContractDataDurabilityPersistent, + Val: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &six, + }, + } + key, entry := getContractDataLedgerEntry(t, data) + assert.NoError(t, writer.UpsertLedgerEntry(entry)) + + expLedgerKey, err := entryKeyToTTLEntryKey(key) + assert.NoError(t, err) + expLegerEntry := getTTLLedgerEntry(expLedgerKey) + assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) + + // Third read transaction, after the first insert has happened in the write transaction + readTx3, err := NewLedgerEntryReader(db).NewTx(context.Background()) + assert.NoError(t, err) + + // Make sure that all the read transactions get an emptyDB error before and after the write transaction is committed + for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { + _, err = readTx.GetLatestLedgerSequence() + assert.Equal(t, ErrEmptyDB, err) + present, _, _, err := GetLedgerEntry(readTx, key) + assert.NoError(t, err) + assert.False(t, present) + } + + // commit the write transaction + ledgerSequence := uint32(23) + assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) + assert.NoError(t, tx.Commit(ledgerSequence)) + + for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { + _, err = readTx.GetLatestLedgerSequence() + assert.Equal(t, ErrEmptyDB, err) + present, _, _, err := GetLedgerEntry(readTx, key) + assert.NoError(t, err) + assert.False(t, present) + } + + // Check that the results are present in the transactions happening after the commit + + obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) + assert.NoError(t, err) + assert.Equal(t, ledgerSequence, obtainedLedgerSequence) + + present, obtainedEntry, obtainedLedgerSequence, expSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) + assert.True(t, present) + require.NotNil(t, expSeq) + assert.Equal(t, ledgerSequence, obtainedLedgerSequence) + assert.Equal(t, six, *obtainedEntry.Data.ContractData.Val.U32) + + for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { + assert.NoError(t, readTx.Done()) + } +} // Check that we can have coexisting reader and writer goroutines without deadlocks or errors func TestConcurrentReadersAndWriter(t *testing.T) { From 557ab81cbfe463ce46b6953e32f4004a8c03537b Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 29 Jul 2024 14:11:55 -0400 Subject: [PATCH 08/37] Remove db.SessionInterface param --- cmd/soroban-rpc/internal/db/db.go | 4 ++-- cmd/soroban-rpc/internal/db/ledgerentry.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index d188ba2c..1ec4b776 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -132,7 +132,7 @@ func getMetaValue(ctx context.Context, q db.SessionInterface, key string) (strin return results[0], nil } -func getLatestLedgerSequence(ctx context.Context, _ db.SessionInterface, ledgerReader LedgerReader, cache *dbCache) (uint32, error) { +func getLatestLedgerSequence(ctx context.Context, ledgerReader LedgerReader, cache *dbCache) (uint32, error) { ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { return 0, err @@ -210,7 +210,7 @@ func NewReadWriter( } func (rw *readWriter) GetLatestLedgerSequence(ctx context.Context) (uint32, error) { - return getLatestLedgerSequence(ctx, rw.db, NewLedgerReader(rw.db), rw.db.cache) + return getLatestLedgerSequence(ctx, NewLedgerReader(rw.db), rw.db.cache) } func (rw *readWriter) NewTx(ctx context.Context) (WriteTx, error) { diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index d163cd6f..2e742752 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -149,7 +149,7 @@ func (l *ledgerEntryReadTx) GetLatestLedgerSequence() (uint32, error) { if l.latestLedgerSeqCache != 0 { return l.latestLedgerSeqCache, nil } - latestLedgerSeq, err := getLatestLedgerSequence(context.Background(), l.tx, l.ledgerReader, l.globalCache) + latestLedgerSeq, err := getLatestLedgerSequence(context.Background(), l.ledgerReader, l.globalCache) if err == nil { l.latestLedgerSeqCache = latestLedgerSeq } @@ -342,7 +342,7 @@ func NewLedgerEntryReader(db *DB) LedgerEntryReader { } func (r ledgerEntryReader) GetLatestLedgerSequence(ctx context.Context) (uint32, error) { - return getLatestLedgerSequence(ctx, r.db, NewLedgerReader(r.db), r.db.cache) + return getLatestLedgerSequence(ctx, NewLedgerReader(r.db), r.db.cache) } // NewCachedTx() caches all accessed ledger entries and select statements. If many ledger entries are accessed, it will grow without bounds. From 39288315e834fff31a4a88fa221656f598f61464 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Thu, 1 Aug 2024 12:48:45 -0400 Subject: [PATCH 09/37] Fix failing unittest --- cmd/soroban-rpc/internal/db/ledgerentry_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index 9831205f..ab28572e 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -357,8 +357,6 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { assert.NoError(t, tx.Commit(ledgerSequence)) for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { - _, err = readTx.GetLatestLedgerSequence() - assert.Equal(t, ErrEmptyDB, err) present, _, _, err := GetLedgerEntry(readTx, key) assert.NoError(t, err) assert.False(t, present) From 4d030da5c173f8bb930d7e004becbe7857832e14 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Fri, 2 Aug 2024 17:09:25 -0400 Subject: [PATCH 10/37] Cache ledger range - 1 --- cmd/soroban-rpc/internal/db/db.go | 19 +++-- cmd/soroban-rpc/internal/db/ledger.go | 79 +++++++++++++++++++ cmd/soroban-rpc/internal/db/ledger_test.go | 22 +++--- .../internal/db/ledgerentry_test.go | 39 +++++---- .../internal/db/transaction_test.go | 4 +- cmd/soroban-rpc/internal/ingest/service.go | 7 +- 6 files changed, 133 insertions(+), 37 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index 1ec4b776..b1d3af4b 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -40,13 +40,14 @@ type WriteTx interface { LedgerEntryWriter() LedgerEntryWriter LedgerWriter() LedgerWriter - Commit(ledgerSeq uint32) error + Commit(ledgerCloseMeta xdr.LedgerCloseMeta) error Rollback() error } type dbCache struct { - latestLedgerSeq uint32 - ledgerEntries transactionalCache // Just like the DB: compress-encoded ledger key -> ledger entry XDR + latestLedgerSeq uint32 + latestLedgerCloseTime int64 + ledgerEntries transactionalCache // Just like the DB: compress-encoded ledger key -> ledger entry XDR sync.RWMutex } @@ -137,7 +138,6 @@ func getLatestLedgerSequence(ctx context.Context, ledgerReader LedgerReader, cac if err != nil { return 0, err } - result := ledgerRange.LastLedger.Sequence // Add missing ledger sequence to the top cache. // Otherwise, the write-through cache won't get updated until the first ingestion commit @@ -145,11 +145,12 @@ func getLatestLedgerSequence(ctx context.Context, ledgerReader LedgerReader, cac if cache.latestLedgerSeq == 0 { // Only update the cache if the value is missing (0), otherwise // we may end up overwriting the entry with an older version - cache.latestLedgerSeq = result + cache.latestLedgerSeq = ledgerRange.LastLedger.Sequence + cache.latestLedgerCloseTime = ledgerRange.LastLedger.CloseTime } cache.Unlock() - return result, nil + return ledgerRange.LastLedger.Sequence, nil } type ReadWriterMetrics struct { @@ -276,7 +277,10 @@ func (w writeTx) TransactionWriter() TransactionWriter { return &w.txWriter } -func (w writeTx) Commit(ledgerSeq uint32) error { +func (w writeTx) Commit(ledgerCloseMeta xdr.LedgerCloseMeta) error { + ledgerSeq := ledgerCloseMeta.LedgerSequence() + ledgerCloseTime := ledgerCloseMeta.LedgerCloseTime() + if err := w.ledgerEntryWriter.flush(); err != nil { return err } @@ -298,6 +302,7 @@ func (w writeTx) Commit(ledgerSeq uint32) error { return err } w.globalCache.latestLedgerSeq = ledgerSeq + w.globalCache.latestLedgerCloseTime = ledgerCloseTime w.ledgerEntryWriter.ledgerEntryCacheWriteTx.commit() return nil } diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index 30a0185c..fe0601ec 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -2,6 +2,7 @@ package db import ( "context" + "database/sql" "fmt" sq "github.com/Masterminds/squirrel" @@ -31,10 +32,88 @@ type ledgerReader struct { db *DB } +type LedgerReaderTx interface { + GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) +} + +type ledgerReaderTx struct { + db *DB + latestLedgerSeqCache uint32 + latestLedgerCloseTimeCache int64 +} + +// GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. +func (r *ledgerReaderTx) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { + if r.latestLedgerSeqCache != 0 { + query := sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName) + var lcm xdr.LedgerCloseMeta + if err := r.db.Select(ctx, &lcm, query); err != nil { + return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err) + } + + return ledgerbucketwindow.LedgerRange{ + FirstLedger: ledgerbucketwindow.LedgerInfo{ + Sequence: lcm.LedgerSequence(), + CloseTime: lcm.LedgerCloseTime(), + }, + LastLedger: ledgerbucketwindow.LedgerInfo{ + Sequence: r.latestLedgerSeqCache, + CloseTime: r.latestLedgerCloseTimeCache, + }, + }, nil + + } + + query := sq.Select("lcm.meta"). + From(ledgerCloseMetaTableName + " as lcm"). + Where(sq.Or{ + sq.Expr("lcm.sequence = (?)", sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName)), + sq.Expr("lcm.sequence = (?)", sq.Select("MAX(sequence)").From(ledgerCloseMetaTableName)), + }).OrderBy("lcm.sequence ASC") + + var lcms []xdr.LedgerCloseMeta + if err := r.db.Select(ctx, &lcms, query); err != nil { + return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err) + } + + // Empty DB + if len(lcms) == 0 { + return ledgerbucketwindow.LedgerRange{}, ErrEmptyDB + } + + r.latestLedgerSeqCache = lcms[len(lcms)-1].LedgerSequence() + r.latestLedgerCloseTimeCache = lcms[len(lcms)-1].LedgerCloseTime() + + return ledgerbucketwindow.LedgerRange{ + FirstLedger: ledgerbucketwindow.LedgerInfo{ + Sequence: lcms[0].LedgerSequence(), + CloseTime: lcms[0].LedgerCloseTime(), + }, + LastLedger: ledgerbucketwindow.LedgerInfo{ + Sequence: lcms[len(lcms)-1].LedgerSequence(), + CloseTime: lcms[len(lcms)-1].LedgerCloseTime(), + }, + }, nil +} + func NewLedgerReader(db *DB) LedgerReader { return ledgerReader{db: db} } +func (r ledgerReader) NewTx(ctx context.Context) (LedgerReaderTx, error) { + txSession := r.db.Clone() + if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { + return nil, err + } + r.db.cache.RLock() + defer r.db.cache.RUnlock() + return &ledgerReaderTx{ + db: r.db, + latestLedgerSeqCache: r.db.cache.latestLedgerSeq, + latestLedgerCloseTimeCache: r.db.cache.latestLedgerCloseTime, + }, nil +} + // StreamAllLedgers runs f over all the ledgers in the database (until f errors or signals it's done). func (r ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error { sql := sq.Select("meta").From(ledgerCloseMetaTableName).OrderBy("sequence asc") diff --git a/cmd/soroban-rpc/internal/db/ledger_test.go b/cmd/soroban-rpc/internal/db/ledger_test.go index e35206ca..1b8b2fc0 100644 --- a/cmd/soroban-rpc/internal/db/ledger_test.go +++ b/cmd/soroban-rpc/internal/db/ledger_test.go @@ -81,8 +81,10 @@ func TestLedgers(t *testing.T) { ledgerSequence := uint32(i) tx, err := NewReadWriter(logger, db, daemon, 150, 15, passphrase).NewTx(context.Background()) require.NoError(t, err) - require.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - require.NoError(t, tx.Commit(ledgerSequence)) + + ledgerCloseMeta := createLedger(ledgerSequence) + require.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + require.NoError(t, tx.Commit(ledgerCloseMeta)) // rolling back after a commit is a no-op require.NoError(t, tx.Rollback()) } @@ -92,16 +94,18 @@ func TestLedgers(t *testing.T) { ledgerSequence := uint32(11) tx, err := NewReadWriter(logger, db, daemon, 150, 15, passphrase).NewTx(context.Background()) require.NoError(t, err) - require.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - require.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta := createLedger(ledgerSequence) + require.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + require.NoError(t, tx.Commit(ledgerCloseMeta)) assertLedgerRange(t, reader, 1, 11) ledgerSequence = uint32(12) tx, err = NewReadWriter(logger, db, daemon, 150, 5, passphrase).NewTx(context.Background()) require.NoError(t, err) - require.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - require.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta = createLedger(ledgerSequence) + require.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + require.NoError(t, tx.Commit(ledgerCloseMeta)) assertLedgerRange(t, reader, 8, 12) } @@ -126,7 +130,7 @@ func TestGetLedgerRange_NonEmptyDB(t *testing.T) { require.NoError(t, ledgerW.InsertLedger(lcm), "ingestion failed for ledger %+v", lcm.V1) require.NoError(t, txW.InsertTransactions(lcm), "ingestion failed for ledger %+v", lcm.V1) } - require.NoError(t, write.Commit(lcms[len(lcms)-1].LedgerSequence())) + require.NoError(t, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) ledgerRange, err := reader.GetLedgerRange(ctx) @@ -154,7 +158,7 @@ func TestGetLedgerRange_SingleDBRow(t *testing.T) { require.NoError(t, ledgerW.InsertLedger(lcm), "ingestion failed for ledger %+v", lcm.V1) require.NoError(t, txW.InsertTransactions(lcm), "ingestion failed for ledger %+v", lcm.V1) } - require.NoError(t, write.Commit(lcms[len(lcms)-1].LedgerSequence())) + require.NoError(t, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) ledgerRange, err := reader.GetLedgerRange(ctx) @@ -196,7 +200,7 @@ func BenchmarkGetLedgerRange(b *testing.B) { require.NoError(b, ledgerW.InsertLedger(lcm)) require.NoError(b, txW.InsertTransactions(lcm)) } - require.NoError(b, write.Commit(lcms[len(lcms)-1].LedgerSequence())) + require.NoError(b, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) b.ResetTimer() diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index ab28572e..d898e2ef 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -88,8 +88,9 @@ func TestGoldenPath(t *testing.T) { assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) ledgerSequence := uint32(23) - assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - assert.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta := createLedger(ledgerSequence) + assert.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + assert.NoError(t, tx.Commit(ledgerCloseMeta)) present, obtainedEntry, obtainedLedgerSequence, liveUntilSeq := getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) @@ -114,8 +115,9 @@ func TestGoldenPath(t *testing.T) { assert.NoError(t, writer.UpsertLedgerEntry(entry)) ledgerSequence = uint32(24) - assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - assert.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta = createLedger(ledgerSequence) + assert.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + assert.NoError(t, tx.Commit(ledgerCloseMeta)) present, obtainedEntry, obtainedLedgerSequence, liveUntilSeq = getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.True(t, present) @@ -131,8 +133,9 @@ func TestGoldenPath(t *testing.T) { assert.NoError(t, writer.DeleteLedgerEntry(key)) ledgerSequence = uint32(25) - assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - assert.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta = createLedger(ledgerSequence) + assert.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + assert.NoError(t, tx.Commit(ledgerCloseMeta)) present, _, obtainedLedgerSequence, liveUntilSeq = getLedgerEntryAndLatestLedgerSequence(t, db, key) assert.False(t, present) @@ -173,8 +176,9 @@ func TestDeleteNonExistentLedgerEmpty(t *testing.T) { key, _ := getContractDataLedgerEntry(t, data) assert.NoError(t, writer.DeleteLedgerEntry(key)) ledgerSequence := uint32(23) - assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - assert.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta := createLedger(ledgerSequence) + assert.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + assert.NoError(t, tx.Commit(ledgerCloseMeta)) // Make sure that the ledger number was submitted obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) @@ -275,8 +279,9 @@ func TestReadTxsDuringWriteTx(t *testing.T) { // Finish the write transaction and check that the results are present ledgerSequence := uint32(23) - assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - assert.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta := createLedger(ledgerSequence) + assert.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + assert.NoError(t, tx.Commit(ledgerCloseMeta)) obtainedLedgerSequence, err := NewLedgerEntryReader(db).GetLatestLedgerSequence(context.Background()) assert.NoError(t, err) @@ -353,8 +358,9 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { // commit the write transaction ledgerSequence := uint32(23) - assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - assert.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta := createLedger(ledgerSequence) + assert.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + assert.NoError(t, tx.Commit(ledgerCloseMeta)) for _, readTx := range []LedgerEntryReadTx{readTx1, readTx2, readTx3} { present, _, _, err := GetLedgerEntry(readTx, key) @@ -420,8 +426,9 @@ func TestConcurrentReadersAndWriter(t *testing.T) { expLegerEntry := getTTLLedgerEntry(expLedgerKey) assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) } - assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence))) - assert.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta := createLedger(ledgerSequence) + assert.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + assert.NoError(t, tx.Commit(ledgerCloseMeta)) logMessageCh <- fmt.Sprintf("Wrote ledger %d", ledgerSequence) time.Sleep(time.Duration(rand.Int31n(30)) * time.Millisecond) } @@ -516,7 +523,7 @@ func benchmarkLedgerEntry(b *testing.B, cached bool) { expLedgerKey, err := entryKeyToTTLEntryKey(key) assert.NoError(b, err) assert.NoError(b, tx.LedgerEntryWriter().UpsertLedgerEntry(getTTLLedgerEntry(expLedgerKey))) - assert.NoError(b, tx.Commit(2)) + assert.NoError(b, tx.Commit(createLedger(2))) reader := NewLedgerEntryReader(db) const numQueriesPerOp = 15 b.ResetTimer() @@ -575,6 +582,6 @@ func BenchmarkLedgerUpdate(b *testing.B) { keyUint32 = xdr.Uint32(j) assert.NoError(b, writer.UpsertLedgerEntry(entry)) } - assert.NoError(b, tx.Commit(uint32(i+1))) + assert.NoError(b, tx.Commit(createLedger(uint32(i+1)))) } } diff --git a/cmd/soroban-rpc/internal/db/transaction_test.go b/cmd/soroban-rpc/internal/db/transaction_test.go index e05671b9..3a14767b 100644 --- a/cmd/soroban-rpc/internal/db/transaction_test.go +++ b/cmd/soroban-rpc/internal/db/transaction_test.go @@ -49,7 +49,7 @@ func TestTransactionFound(t *testing.T) { require.NoError(t, ledgerW.InsertLedger(lcm), "ingestion failed for ledger %+v", lcm.V1) require.NoError(t, txW.InsertTransactions(lcm), "ingestion failed for ledger %+v", lcm.V1) } - require.NoError(t, write.Commit(lcms[len(lcms)-1].LedgerSequence())) + require.NoError(t, write.Commit(lcms[len(lcms)-1])) // check 404 case reader := NewTransactionReader(log, db, passphrase) @@ -89,7 +89,7 @@ func BenchmarkTransactionFetch(b *testing.B) { require.NoError(b, ledgerW.InsertLedger(lcm)) require.NoError(b, txW.InsertTransactions(lcm)) } - require.NoError(b, write.Commit(lcms[len(lcms)-1].LedgerSequence())) + require.NoError(b, write.Commit(lcms[len(lcms)-1])) reader := NewTransactionReader(log, db, passphrase) randoms := make([]int, b.N) diff --git a/cmd/soroban-rpc/internal/ingest/service.go b/cmd/soroban-rpc/internal/ingest/service.go index 86a2b445..69f87b0a 100644 --- a/cmd/soroban-rpc/internal/ingest/service.go +++ b/cmd/soroban-rpc/internal/ingest/service.go @@ -245,14 +245,15 @@ func (s *Service) fillEntriesFromCheckpoint(ctx context.Context, archive history if err := <-prepareRangeErr; err != nil { return err } - if ledgerCloseMeta, err := s.ledgerBackend.GetLedger(ctx, checkpointLedger); err != nil { + var ledgerCloseMeta xdr.LedgerCloseMeta + if ledgerCloseMeta, err = s.ledgerBackend.GetLedger(ctx, checkpointLedger); err != nil { return err } else if err = reader.VerifyBucketList(ledgerCloseMeta.BucketListHash()); err != nil { return err } s.logger.Info("committing checkpoint ledger entries") - err = tx.Commit(checkpointLedger) + err = tx.Commit(ledgerCloseMeta) transactionCommitted = true if err != nil { return err @@ -306,7 +307,7 @@ func (s *Service) ingest(ctx context.Context, sequence uint32) error { return err } - if err := tx.Commit(sequence); err != nil { + if err := tx.Commit(ledgerCloseMeta); err != nil { return err } s.logger. From 9346660310786a1c43459627e59403da1b72f5b9 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Fri, 2 Aug 2024 19:50:51 -0400 Subject: [PATCH 11/37] Cache ledger range - 2 --- cmd/soroban-rpc/internal/db/db.go | 7 +- cmd/soroban-rpc/internal/db/ledger.go | 72 ++++++++++--------- cmd/soroban-rpc/internal/db/ledger_test.go | 20 ++++-- cmd/soroban-rpc/internal/db/mocks.go | 8 +-- .../internal/methods/get_fee_stats.go | 7 +- .../methods/get_latest_ledger_test.go | 5 -- .../internal/methods/get_transaction.go | 7 +- .../internal/methods/get_transactions.go | 7 +- cmd/soroban-rpc/internal/methods/health.go | 7 +- .../internal/methods/send_transaction.go | 9 ++- 10 files changed, 95 insertions(+), 54 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index b1d3af4b..177f86f3 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -134,7 +134,12 @@ func getMetaValue(ctx context.Context, q db.SessionInterface, key string) (strin } func getLatestLedgerSequence(ctx context.Context, ledgerReader LedgerReader, cache *dbCache) (uint32, error) { - ledgerRange, err := ledgerReader.GetLedgerRange(ctx) + tx, err := ledgerReader.NewTx(ctx) + if err != nil { + return 0, err + } + + ledgerRange, err := tx.GetLedgerRange(ctx) if err != nil { return 0, err } diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index fe0601ec..36916708 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -21,7 +21,7 @@ type StreamLedgerFn func(xdr.LedgerCloseMeta) error type LedgerReader interface { GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error - GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) + NewTx(ctx context.Context) (LedgerReaderTx, error) } type LedgerWriter interface { @@ -45,16 +45,20 @@ type ledgerReaderTx struct { // GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. func (r *ledgerReaderTx) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { if r.latestLedgerSeqCache != 0 { - query := sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName) - var lcm xdr.LedgerCloseMeta + query := sq.Select("meta"). + From(ledgerCloseMetaTableName). + Where( + sq.Expr("sequence = (?)", sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName)), + ) + var lcm []xdr.LedgerCloseMeta if err := r.db.Select(ctx, &lcm, query); err != nil { return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err) } return ledgerbucketwindow.LedgerRange{ FirstLedger: ledgerbucketwindow.LedgerInfo{ - Sequence: lcm.LedgerSequence(), - CloseTime: lcm.LedgerCloseTime(), + Sequence: lcm[0].LedgerSequence(), + CloseTime: lcm[0].LedgerCloseTime(), }, LastLedger: ledgerbucketwindow.LedgerInfo{ Sequence: r.latestLedgerSeqCache, @@ -153,35 +157,35 @@ func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.Ledge } // GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. -func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { - query := sq.Select("lcm.meta"). - From(ledgerCloseMetaTableName + " as lcm"). - Where(sq.Or{ - sq.Expr("lcm.sequence = (?)", sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName)), - sq.Expr("lcm.sequence = (?)", sq.Select("MAX(sequence)").From(ledgerCloseMetaTableName)), - }).OrderBy("lcm.sequence ASC") - - var lcms []xdr.LedgerCloseMeta - if err := r.db.Select(ctx, &lcms, query); err != nil { - return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err) - } - - // Empty DB - if len(lcms) == 0 { - return ledgerbucketwindow.LedgerRange{}, ErrEmptyDB - } - - return ledgerbucketwindow.LedgerRange{ - FirstLedger: ledgerbucketwindow.LedgerInfo{ - Sequence: lcms[0].LedgerSequence(), - CloseTime: lcms[0].LedgerCloseTime(), - }, - LastLedger: ledgerbucketwindow.LedgerInfo{ - Sequence: lcms[len(lcms)-1].LedgerSequence(), - CloseTime: lcms[len(lcms)-1].LedgerCloseTime(), - }, - }, nil -} +//func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { +// query := sq.Select("lcm.meta"). +// From(ledgerCloseMetaTableName + " as lcm"). +// Where(sq.Or{ +// sq.Expr("lcm.sequence = (?)", sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName)), +// sq.Expr("lcm.sequence = (?)", sq.Select("MAX(sequence)").From(ledgerCloseMetaTableName)), +// }).OrderBy("lcm.sequence ASC") +// +// var lcms []xdr.LedgerCloseMeta +// if err := r.db.Select(ctx, &lcms, query); err != nil { +// return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err) +// } +// +// // Empty DB +// if len(lcms) == 0 { +// return ledgerbucketwindow.LedgerRange{}, ErrEmptyDB +// } +// +// return ledgerbucketwindow.LedgerRange{ +// FirstLedger: ledgerbucketwindow.LedgerInfo{ +// Sequence: lcms[0].LedgerSequence(), +// CloseTime: lcms[0].LedgerCloseTime(), +// }, +// LastLedger: ledgerbucketwindow.LedgerInfo{ +// Sequence: lcms[len(lcms)-1].LedgerSequence(), +// CloseTime: lcms[len(lcms)-1].LedgerCloseTime(), +// }, +// }, nil +//} type ledgerWriter struct { stmtCache *sq.StmtCache diff --git a/cmd/soroban-rpc/internal/db/ledger_test.go b/cmd/soroban-rpc/internal/db/ledger_test.go index 1b8b2fc0..6e86ad61 100644 --- a/cmd/soroban-rpc/internal/db/ledger_test.go +++ b/cmd/soroban-rpc/internal/db/ledger_test.go @@ -133,7 +133,10 @@ func TestGetLedgerRange_NonEmptyDB(t *testing.T) { require.NoError(t, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) - ledgerRange, err := reader.GetLedgerRange(ctx) + tx, err := reader.NewTx(ctx) + require.NoError(t, err) + + ledgerRange, err := tx.GetLedgerRange(ctx) require.NoError(t, err) assert.Equal(t, uint32(1334), ledgerRange.FirstLedger.Sequence) assert.Equal(t, ledgerCloseTime(1334), ledgerRange.FirstLedger.CloseTime) @@ -161,7 +164,10 @@ func TestGetLedgerRange_SingleDBRow(t *testing.T) { require.NoError(t, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) - ledgerRange, err := reader.GetLedgerRange(ctx) + tx, err := reader.NewTx(ctx) + require.NoError(t, err) + + ledgerRange, err := tx.GetLedgerRange(ctx) require.NoError(t, err) assert.Equal(t, uint32(1334), ledgerRange.FirstLedger.Sequence) assert.Equal(t, ledgerCloseTime(1334), ledgerRange.FirstLedger.CloseTime) @@ -174,7 +180,10 @@ func TestGetLedgerRange_EmptyDB(t *testing.T) { ctx := context.TODO() reader := NewLedgerReader(db) - ledgerRange, err := reader.GetLedgerRange(ctx) + tx, err := reader.NewTx(ctx) + require.NoError(t, err) + + ledgerRange, err := tx.GetLedgerRange(ctx) assert.Equal(t, ErrEmptyDB, err) assert.Equal(t, uint32(0), ledgerRange.FirstLedger.Sequence) assert.Equal(t, int64(0), ledgerRange.FirstLedger.CloseTime) @@ -185,6 +194,7 @@ func TestGetLedgerRange_EmptyDB(t *testing.T) { func BenchmarkGetLedgerRange(b *testing.B) { db := NewTestDB(b) logger := log.DefaultLogger + ctx := context.TODO() writer := NewReadWriter(logger, db, interfaces.MakeNoOpDeamon(), 100, 1_000_000, passphrase) write, err := writer.NewTx(context.TODO()) require.NoError(b, err) @@ -202,10 +212,12 @@ func BenchmarkGetLedgerRange(b *testing.B) { } require.NoError(b, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) + tx, err := reader.NewTx(ctx) + require.NoError(b, err) b.ResetTimer() for range b.N { - ledgerRange, err := reader.GetLedgerRange(context.TODO()) + ledgerRange, err := tx.GetLedgerRange(context.TODO()) require.NoError(b, err) assert.Equal(b, lcms[0].LedgerSequence(), ledgerRange.FirstLedger.Sequence) assert.Equal(b, lcms[len(lcms)-1].LedgerSequence(), ledgerRange.LastLedger.Sequence) diff --git a/cmd/soroban-rpc/internal/db/mocks.go b/cmd/soroban-rpc/internal/db/mocks.go index 492d64bb..b8e8dcba 100644 --- a/cmd/soroban-rpc/internal/db/mocks.go +++ b/cmd/soroban-rpc/internal/db/mocks.go @@ -83,6 +83,10 @@ type MockLedgerReader struct { txn *MockTransactionHandler } +func (m *MockLedgerReader) NewTx(ctx context.Context) (LedgerReaderTx, error) { + return nil, nil +} + func NewMockLedgerReader(txn *MockTransactionHandler) *MockLedgerReader { return &MockLedgerReader{ txn: txn, @@ -101,10 +105,6 @@ func (m *MockLedgerReader) StreamAllLedgers(_ context.Context, _ StreamLedgerFn) return nil } -func (m *MockLedgerReader) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { - return m.txn.ledgerRange, nil -} - var ( _ TransactionReader = &MockTransactionHandler{} _ TransactionWriter = &MockTransactionHandler{} diff --git a/cmd/soroban-rpc/internal/methods/get_fee_stats.go b/cmd/soroban-rpc/internal/methods/get_fee_stats.go index 9fd85d76..d6d0e8e0 100644 --- a/cmd/soroban-rpc/internal/methods/get_fee_stats.go +++ b/cmd/soroban-rpc/internal/methods/get_fee_stats.go @@ -62,7 +62,12 @@ func NewGetFeeStatsHandler(windows *feewindow.FeeWindows, ledgerReader db.Ledger logger *log.Entry, ) jrpc2.Handler { return NewHandler(func(ctx context.Context) (GetFeeStatsResult, error) { - ledgerRange, err := ledgerReader.GetLedgerRange(ctx) + tx, err := ledgerReader.NewTx(ctx) + if err != nil { + return GetFeeStatsResult{}, err + } + + ledgerRange, err := tx.GetLedgerRange(ctx) if err != nil { // still not fatal logger.WithError(err). Error("could not fetch ledger range") diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go index affa0536..f686f593 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go @@ -11,7 +11,6 @@ import ( "github.com/stellar/go/xdr" "github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/db" - "github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/ledgerbucketwindow" ) const ( @@ -26,10 +25,6 @@ type ConstantLedgerEntryReaderTx struct{} type ConstantLedgerReader struct{} -func (ledgerReader *ConstantLedgerReader) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { - return ledgerbucketwindow.LedgerRange{}, nil -} - func (entryReader *ConstantLedgerEntryReader) GetLatestLedgerSequence(_ context.Context) (uint32, error) { return expectedLatestLedgerSequence, nil } diff --git a/cmd/soroban-rpc/internal/methods/get_transaction.go b/cmd/soroban-rpc/internal/methods/get_transaction.go index ac6ac8b2..f62666df 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction.go @@ -92,7 +92,12 @@ func GetTransaction( } } - storeRange, err := ledgerReader.GetLedgerRange(ctx) + tx, err := ledgerReader.NewTx(ctx) + if err != nil { + return GetTransactionResponse{}, err + } + + storeRange, err := tx.GetLedgerRange(ctx) if err != nil { return GetTransactionResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, diff --git a/cmd/soroban-rpc/internal/methods/get_transactions.go b/cmd/soroban-rpc/internal/methods/get_transactions.go index 13ff359f..6eab9474 100644 --- a/cmd/soroban-rpc/internal/methods/get_transactions.go +++ b/cmd/soroban-rpc/internal/methods/get_transactions.go @@ -214,7 +214,12 @@ func (h transactionsRPCHandler) processTransactionsInLedger(ledger xdr.LedgerClo func (h transactionsRPCHandler) getTransactionsByLedgerSequence(ctx context.Context, request GetTransactionsRequest, ) (GetTransactionsResponse, error) { - ledgerRange, err := h.ledgerReader.GetLedgerRange(ctx) + tx, err := h.ledgerReader.NewTx(ctx) + if err != nil { + return GetTransactionsResponse{}, err + } + + ledgerRange, err := tx.GetLedgerRange(ctx) if err != nil { return GetTransactionsResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, diff --git a/cmd/soroban-rpc/internal/methods/health.go b/cmd/soroban-rpc/internal/methods/health.go index 8de0767c..dcef483e 100644 --- a/cmd/soroban-rpc/internal/methods/health.go +++ b/cmd/soroban-rpc/internal/methods/health.go @@ -24,7 +24,12 @@ func NewHealthCheck( maxHealthyLedgerLatency time.Duration, ) jrpc2.Handler { return NewHandler(func(ctx context.Context) (HealthCheckResult, error) { - ledgerRange, err := ledgerReader.GetLedgerRange(ctx) + tx, err := ledgerReader.NewTx(ctx) + if err != nil { + return HealthCheckResult{}, err + } + + ledgerRange, err := tx.GetLedgerRange(ctx) if err != nil || ledgerRange.LastLedger.Sequence < 1 { extra := "" if err != nil { diff --git a/cmd/soroban-rpc/internal/methods/send_transaction.go b/cmd/soroban-rpc/internal/methods/send_transaction.go index 9cdd6a20..98a16982 100644 --- a/cmd/soroban-rpc/internal/methods/send_transaction.go +++ b/cmd/soroban-rpc/internal/methods/send_transaction.go @@ -73,13 +73,18 @@ func NewSendTransactionHandler( } txHash := hex.EncodeToString(hash[:]) - ledgerInfo, err := ledgerReader.GetLedgerRange(ctx) + tx, err := ledgerReader.NewTx(ctx) + if err != nil { + return SendTransactionResponse{}, err + } + + ledgerRange, err := tx.GetLedgerRange(ctx) if err != nil { // still not fatal logger.WithError(err). WithField("tx", request.Transaction). Error("could not fetch ledger range") } - latestLedgerInfo := ledgerInfo.LastLedger + latestLedgerInfo := ledgerRange.LastLedger resp, err := submitter.SubmitTransaction(ctx, request.Transaction) if err != nil { From f47fe65e074cc7a569dd7cfb4f782b151554c5ea Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Fri, 2 Aug 2024 19:57:53 -0400 Subject: [PATCH 12/37] Cache ledger range - 3 --- cmd/soroban-rpc/internal/db/db.go | 2 +- cmd/soroban-rpc/internal/methods/get_fee_stats.go | 6 +++++- cmd/soroban-rpc/internal/methods/get_transaction.go | 5 ++++- cmd/soroban-rpc/internal/methods/get_transactions.go | 5 ++++- cmd/soroban-rpc/internal/methods/health.go | 5 ++++- cmd/soroban-rpc/internal/methods/send_transaction.go | 6 +++++- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index 177f86f3..8392db9d 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -144,7 +144,7 @@ func getLatestLedgerSequence(ctx context.Context, ledgerReader LedgerReader, cac return 0, err } - // Add missing ledger sequence to the top cache. + // Add missing ledger sequence and close time to the top cache. // Otherwise, the write-through cache won't get updated until the first ingestion commit cache.Lock() if cache.latestLedgerSeq == 0 { diff --git a/cmd/soroban-rpc/internal/methods/get_fee_stats.go b/cmd/soroban-rpc/internal/methods/get_fee_stats.go index d6d0e8e0..a22dbd31 100644 --- a/cmd/soroban-rpc/internal/methods/get_fee_stats.go +++ b/cmd/soroban-rpc/internal/methods/get_fee_stats.go @@ -2,6 +2,7 @@ package methods import ( "context" + "fmt" "github.com/creachadair/jrpc2" @@ -64,7 +65,10 @@ func NewGetFeeStatsHandler(windows *feewindow.FeeWindows, ledgerReader db.Ledger return NewHandler(func(ctx context.Context) (GetFeeStatsResult, error) { tx, err := ledgerReader.NewTx(ctx) if err != nil { - return GetFeeStatsResult{}, err + return GetFeeStatsResult{}, jrpc2.Error{ + Code: jrpc2.InternalError, + Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + } } ledgerRange, err := tx.GetLedgerRange(ctx) diff --git a/cmd/soroban-rpc/internal/methods/get_transaction.go b/cmd/soroban-rpc/internal/methods/get_transaction.go index f62666df..77c27ab8 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction.go @@ -94,7 +94,10 @@ func GetTransaction( tx, err := ledgerReader.NewTx(ctx) if err != nil { - return GetTransactionResponse{}, err + return GetTransactionResponse{}, jrpc2.Error{ + Code: jrpc2.InternalError, + Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + } } storeRange, err := tx.GetLedgerRange(ctx) diff --git a/cmd/soroban-rpc/internal/methods/get_transactions.go b/cmd/soroban-rpc/internal/methods/get_transactions.go index 6eab9474..a18c5ae4 100644 --- a/cmd/soroban-rpc/internal/methods/get_transactions.go +++ b/cmd/soroban-rpc/internal/methods/get_transactions.go @@ -216,7 +216,10 @@ func (h transactionsRPCHandler) getTransactionsByLedgerSequence(ctx context.Cont ) (GetTransactionsResponse, error) { tx, err := h.ledgerReader.NewTx(ctx) if err != nil { - return GetTransactionsResponse{}, err + return GetTransactionsResponse{}, jrpc2.Error{ + Code: jrpc2.InternalError, + Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + } } ledgerRange, err := tx.GetLedgerRange(ctx) diff --git a/cmd/soroban-rpc/internal/methods/health.go b/cmd/soroban-rpc/internal/methods/health.go index dcef483e..1006029c 100644 --- a/cmd/soroban-rpc/internal/methods/health.go +++ b/cmd/soroban-rpc/internal/methods/health.go @@ -26,7 +26,10 @@ func NewHealthCheck( return NewHandler(func(ctx context.Context) (HealthCheckResult, error) { tx, err := ledgerReader.NewTx(ctx) if err != nil { - return HealthCheckResult{}, err + return HealthCheckResult{}, jrpc2.Error{ + Code: jrpc2.InternalError, + Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + } } ledgerRange, err := tx.GetLedgerRange(ctx) diff --git a/cmd/soroban-rpc/internal/methods/send_transaction.go b/cmd/soroban-rpc/internal/methods/send_transaction.go index 98a16982..579b07f2 100644 --- a/cmd/soroban-rpc/internal/methods/send_transaction.go +++ b/cmd/soroban-rpc/internal/methods/send_transaction.go @@ -3,6 +3,7 @@ package methods import ( "context" "encoding/hex" + "fmt" "github.com/creachadair/jrpc2" @@ -75,7 +76,10 @@ func NewSendTransactionHandler( tx, err := ledgerReader.NewTx(ctx) if err != nil { - return SendTransactionResponse{}, err + return SendTransactionResponse{}, jrpc2.Error{ + Code: jrpc2.InternalError, + Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + } } ledgerRange, err := tx.GetLedgerRange(ctx) From a81e0cf43d639d64c5b188a5a6b4bf9659e756e8 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 5 Aug 2024 11:12:06 -0400 Subject: [PATCH 13/37] Cache ledger range - 4 --- cmd/soroban-rpc/internal/db/mocks.go | 18 ++++++++++++++---- .../internal/methods/get_latest_ledger_test.go | 6 ++++++ .../internal/methods/get_transaction.go | 4 ++-- cmd/soroban-rpc/internal/methods/util_test.go | 10 ++++++---- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/mocks.go b/cmd/soroban-rpc/internal/db/mocks.go index b8e8dcba..2b001d6a 100644 --- a/cmd/soroban-rpc/internal/db/mocks.go +++ b/cmd/soroban-rpc/internal/db/mocks.go @@ -79,12 +79,16 @@ func (txn *MockTransactionHandler) GetTransaction(_ context.Context, hash xdr.Ha func (txn *MockTransactionHandler) RegisterMetrics(_, _ prometheus.Observer) {} -type MockLedgerReader struct { - txn *MockTransactionHandler +type MockLedgerReaderTx struct { + reader *MockLedgerReader +} + +func (tx *MockLedgerReaderTx) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { + return tx.reader.txn.ledgerRange, nil } -func (m *MockLedgerReader) NewTx(ctx context.Context) (LedgerReaderTx, error) { - return nil, nil +type MockLedgerReader struct { + txn *MockTransactionHandler } func NewMockLedgerReader(txn *MockTransactionHandler) *MockLedgerReader { @@ -93,6 +97,12 @@ func NewMockLedgerReader(txn *MockTransactionHandler) *MockLedgerReader { } } +func (m *MockLedgerReader) NewTx(_ context.Context) (LedgerReaderTx, error) { + return &MockLedgerReaderTx{ + reader: m, + }, nil +} + func (m *MockLedgerReader) GetLedger(_ context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) { lcm, ok := m.txn.ledgerSeqToMeta[sequence] if !ok { diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go index f686f593..c0e16cb4 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go @@ -25,6 +25,10 @@ type ConstantLedgerEntryReaderTx struct{} type ConstantLedgerReader struct{} +func (ledgerReader *ConstantLedgerReader) NewTx(ctx context.Context) (db.LedgerReaderTx, error) { + return nil, nil +} + func (entryReader *ConstantLedgerEntryReader) GetLatestLedgerSequence(_ context.Context) (uint32, error) { return expectedLatestLedgerSequence, nil } @@ -86,3 +90,5 @@ func TestGetLatestLedger(t *testing.T) { assert.Equal(t, expectedLatestLedgerProtocolVersion, latestLedgerResp.ProtocolVersion) assert.Equal(t, expectedLatestLedgerSequence, latestLedgerResp.Sequence) } + +var _ db.LedgerReader = &ConstantLedgerReader{} diff --git a/cmd/soroban-rpc/internal/methods/get_transaction.go b/cmd/soroban-rpc/internal/methods/get_transaction.go index 77c27ab8..0a89c066 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction.go @@ -92,7 +92,7 @@ func GetTransaction( } } - tx, err := ledgerReader.NewTx(ctx) + ledgerTx, err := ledgerReader.NewTx(ctx) if err != nil { return GetTransactionResponse{}, jrpc2.Error{ Code: jrpc2.InternalError, @@ -100,7 +100,7 @@ func GetTransaction( } } - storeRange, err := tx.GetLedgerRange(ctx) + storeRange, err := ledgerTx.GetLedgerRange(ctx) if err != nil { return GetTransactionResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, diff --git a/cmd/soroban-rpc/internal/methods/util_test.go b/cmd/soroban-rpc/internal/methods/util_test.go index 520a1bae..b183a318 100644 --- a/cmd/soroban-rpc/internal/methods/util_test.go +++ b/cmd/soroban-rpc/internal/methods/util_test.go @@ -27,8 +27,9 @@ func BenchmarkGetProtocolVersion(b *testing.B) { ledgerSequence := uint32(1) tx, err := db.NewReadWriter(log.DefaultLogger, dbx, daemon, 150, 15, "passphrase").NewTx(context.Background()) require.NoError(b, err) - require.NoError(b, tx.LedgerWriter().InsertLedger(createMockLedgerCloseMeta(ledgerSequence))) - require.NoError(b, tx.Commit(ledgerSequence)) + ledgerCloseMeta := createMockLedgerCloseMeta(ledgerSequence) + require.NoError(b, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + require.NoError(b, tx.Commit(ledgerCloseMeta)) ledgerEntryReader := db.NewLedgerEntryReader(dbx) b.ResetTimer() @@ -52,8 +53,9 @@ func TestGetProtocolVersion(t *testing.T) { ledgerSequence := uint32(1) tx, err := db.NewReadWriter(log.DefaultLogger, dbx, daemon, 150, 15, "passphrase").NewTx(context.Background()) require.NoError(t, err) - require.NoError(t, tx.LedgerWriter().InsertLedger(createMockLedgerCloseMeta(ledgerSequence))) - require.NoError(t, tx.Commit(ledgerSequence)) + ledgerCloseMeta := createMockLedgerCloseMeta(ledgerSequence) + require.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + require.NoError(t, tx.Commit(ledgerCloseMeta)) ledgerEntryReader := db.NewLedgerEntryReader(dbx) protocolVersion, err := getProtocolVersion(context.TODO(), ledgerEntryReader, ledgerReader) From 1b41b5b2b6c8a73d8979509024983cca861fe02a Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 5 Aug 2024 16:27:09 -0400 Subject: [PATCH 14/37] Fix failing test --- cmd/soroban-rpc/internal/db/ledger.go | 32 +------------------ .../internal/ingest/mock_db_test.go | 4 +-- .../internal/preflight/preflight_test.go | 5 +-- 3 files changed, 6 insertions(+), 35 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index 36916708..6fc559f6 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -44,6 +44,7 @@ type ledgerReaderTx struct { // GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. func (r *ledgerReaderTx) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { + // Make use of the cached latest ledger seq and close time to query only the oldest ledger details. if r.latestLedgerSeqCache != 0 { query := sq.Select("meta"). From(ledgerCloseMetaTableName). @@ -156,37 +157,6 @@ func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.Ledge } } -// GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. -//func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { -// query := sq.Select("lcm.meta"). -// From(ledgerCloseMetaTableName + " as lcm"). -// Where(sq.Or{ -// sq.Expr("lcm.sequence = (?)", sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName)), -// sq.Expr("lcm.sequence = (?)", sq.Select("MAX(sequence)").From(ledgerCloseMetaTableName)), -// }).OrderBy("lcm.sequence ASC") -// -// var lcms []xdr.LedgerCloseMeta -// if err := r.db.Select(ctx, &lcms, query); err != nil { -// return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err) -// } -// -// // Empty DB -// if len(lcms) == 0 { -// return ledgerbucketwindow.LedgerRange{}, ErrEmptyDB -// } -// -// return ledgerbucketwindow.LedgerRange{ -// FirstLedger: ledgerbucketwindow.LedgerInfo{ -// Sequence: lcms[0].LedgerSequence(), -// CloseTime: lcms[0].LedgerCloseTime(), -// }, -// LastLedger: ledgerbucketwindow.LedgerInfo{ -// Sequence: lcms[len(lcms)-1].LedgerSequence(), -// CloseTime: lcms[len(lcms)-1].LedgerCloseTime(), -// }, -// }, nil -//} - type ledgerWriter struct { stmtCache *sq.StmtCache } diff --git a/cmd/soroban-rpc/internal/ingest/mock_db_test.go b/cmd/soroban-rpc/internal/ingest/mock_db_test.go index 6e57658d..d9fd347f 100644 --- a/cmd/soroban-rpc/internal/ingest/mock_db_test.go +++ b/cmd/soroban-rpc/internal/ingest/mock_db_test.go @@ -51,8 +51,8 @@ func (m MockTx) TransactionWriter() db.TransactionWriter { return args.Get(0).(db.TransactionWriter) } -func (m MockTx) Commit(ledgerSeq uint32) error { - args := m.Called(ledgerSeq) +func (m MockTx) Commit(ledgerCloseMeta xdr.LedgerCloseMeta) error { + args := m.Called(ledgerCloseMeta) return args.Error(0) } diff --git a/cmd/soroban-rpc/internal/preflight/preflight_test.go b/cmd/soroban-rpc/internal/preflight/preflight_test.go index fc45a9a1..f99d489c 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight_test.go +++ b/cmd/soroban-rpc/internal/preflight/preflight_test.go @@ -326,8 +326,9 @@ func getDB(t testing.TB, restartDB bool) *db.DB { err := tx.LedgerEntryWriter().UpsertLedgerEntry(e) require.NoError(t, err) } - require.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(2))) - require.NoError(t, tx.Commit(2)) + ledgerCloseMeta := createLedger(uint32(2)) + require.NoError(t, tx.LedgerWriter().InsertLedger(ledgerCloseMeta)) + require.NoError(t, tx.Commit(ledgerCloseMeta)) if restartDB { // Restarting the DB resets the ledger entries write-through cache From bb06fdfcc2c2bc6b63398357917c6f2c8a3a41bd Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 5 Aug 2024 16:39:05 -0400 Subject: [PATCH 15/37] Fix failing test - 2 --- cmd/soroban-rpc/internal/ingest/service_test.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd/soroban-rpc/internal/ingest/service_test.go b/cmd/soroban-rpc/internal/ingest/service_test.go index dbeeb1f6..0101c529 100644 --- a/cmd/soroban-rpc/internal/ingest/service_test.go +++ b/cmd/soroban-rpc/internal/ingest/service_test.go @@ -82,12 +82,6 @@ func TestIngestion(t *testing.T) { mockLedgerWriter := &MockLedgerWriter{} mockTxWriter := &MockTransactionWriter{} ctx := context.Background() - mockDB.On("NewTx", ctx).Return(mockTx, nil).Once() - mockTx.On("Commit", sequence).Return(nil).Once() - mockTx.On("Rollback").Return(nil).Once() - mockTx.On("LedgerEntryWriter").Return(mockLedgerEntryWriter).Twice() - mockTx.On("LedgerWriter").Return(mockLedgerWriter).Once() - mockTx.On("TransactionWriter").Return(mockTxWriter).Once() src := xdr.MustAddress("GBXGQJWVLWOYHFLVTKWV5FGHA3LNYY2JQKM7OAJAUEQFU6LPCSEFVXON") firstTx := xdr.TransactionEnvelope{ @@ -243,6 +237,13 @@ func TestIngestion(t *testing.T) { EvictedPersistentLedgerEntries: []xdr.LedgerEntry{evictedPersistentLedgerEntry}, }, } + mockDB.On("NewTx", ctx).Return(mockTx, nil).Once() + mockTx.On("Commit", ledger).Return(nil).Once() + mockTx.On("Rollback").Return(nil).Once() + mockTx.On("LedgerEntryWriter").Return(mockLedgerEntryWriter).Twice() + mockTx.On("LedgerWriter").Return(mockLedgerWriter).Once() + mockTx.On("TransactionWriter").Return(mockTxWriter).Once() + mockLedgerBackend.On("GetLedger", ctx, sequence).Return(ledger, nil).Once() mockLedgerEntryWriter.On("UpsertLedgerEntry", operationChanges[1].MustUpdated()). Return(nil).Once() From 844d46be860e4a6d5bc58da41ea537d09a595473 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Tue, 6 Aug 2024 11:35:12 -0400 Subject: [PATCH 16/37] Fix linting - 1 --- cmd/soroban-rpc/internal/db/ledger.go | 1 - .../internal/ingest/mock_db_test.go | 24 +++++++++---------- .../internal/methods/get_fee_stats.go | 2 +- .../methods/get_latest_ledger_test.go | 11 +++++++-- .../internal/methods/get_transaction.go | 2 +- .../internal/methods/get_transactions.go | 2 +- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index 6fc559f6..0550ded2 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -66,7 +66,6 @@ func (r *ledgerReaderTx) GetLedgerRange(ctx context.Context) (ledgerbucketwindow CloseTime: r.latestLedgerCloseTimeCache, }, }, nil - } query := sq.Select("lcm.meta"). diff --git a/cmd/soroban-rpc/internal/ingest/mock_db_test.go b/cmd/soroban-rpc/internal/ingest/mock_db_test.go index d9fd347f..e9d632d5 100644 --- a/cmd/soroban-rpc/internal/ingest/mock_db_test.go +++ b/cmd/soroban-rpc/internal/ingest/mock_db_test.go @@ -22,12 +22,12 @@ type MockDB struct { mock.Mock } -func (m MockDB) NewTx(ctx context.Context) (db.WriteTx, error) { +func (m *MockDB) NewTx(ctx context.Context) (db.WriteTx, error) { args := m.Called(ctx) return args.Get(0).(db.WriteTx), args.Error(1) } -func (m MockDB) GetLatestLedgerSequence(ctx context.Context) (uint32, error) { +func (m *MockDB) GetLatestLedgerSequence(ctx context.Context) (uint32, error) { args := m.Called(ctx) return args.Get(0).(uint32), args.Error(1) } @@ -36,27 +36,27 @@ type MockTx struct { mock.Mock } -func (m MockTx) LedgerEntryWriter() db.LedgerEntryWriter { +func (m *MockTx) LedgerEntryWriter() db.LedgerEntryWriter { args := m.Called() return args.Get(0).(db.LedgerEntryWriter) } -func (m MockTx) LedgerWriter() db.LedgerWriter { +func (m *MockTx) LedgerWriter() db.LedgerWriter { args := m.Called() return args.Get(0).(db.LedgerWriter) } -func (m MockTx) TransactionWriter() db.TransactionWriter { +func (m *MockTx) TransactionWriter() db.TransactionWriter { args := m.Called() return args.Get(0).(db.TransactionWriter) } -func (m MockTx) Commit(ledgerCloseMeta xdr.LedgerCloseMeta) error { +func (m *MockTx) Commit(ledgerCloseMeta xdr.LedgerCloseMeta) error { args := m.Called(ledgerCloseMeta) return args.Error(0) } -func (m MockTx) Rollback() error { +func (m *MockTx) Rollback() error { args := m.Called() return args.Error(0) } @@ -65,12 +65,12 @@ type MockLedgerEntryWriter struct { mock.Mock } -func (m MockLedgerEntryWriter) UpsertLedgerEntry(entry xdr.LedgerEntry) error { +func (m *MockLedgerEntryWriter) UpsertLedgerEntry(entry xdr.LedgerEntry) error { args := m.Called(entry) return args.Error(0) } -func (m MockLedgerEntryWriter) DeleteLedgerEntry(key xdr.LedgerKey) error { +func (m *MockLedgerEntryWriter) DeleteLedgerEntry(key xdr.LedgerKey) error { args := m.Called(key) return args.Error(0) } @@ -79,7 +79,7 @@ type MockLedgerWriter struct { mock.Mock } -func (m MockLedgerWriter) InsertLedger(ledger xdr.LedgerCloseMeta) error { +func (m *MockLedgerWriter) InsertLedger(ledger xdr.LedgerCloseMeta) error { args := m.Called(ledger) return args.Error(0) } @@ -88,11 +88,11 @@ type MockTransactionWriter struct { mock.Mock } -func (m MockTransactionWriter) InsertTransactions(ledger xdr.LedgerCloseMeta) error { +func (m *MockTransactionWriter) InsertTransactions(ledger xdr.LedgerCloseMeta) error { args := m.Called(ledger) return args.Error(0) } -func (m MockTransactionWriter) RegisterMetrics(ingest, count prometheus.Observer) { +func (m *MockTransactionWriter) RegisterMetrics(ingest, count prometheus.Observer) { m.Called(ingest, count) } diff --git a/cmd/soroban-rpc/internal/methods/get_fee_stats.go b/cmd/soroban-rpc/internal/methods/get_fee_stats.go index a22dbd31..5d5cf917 100644 --- a/cmd/soroban-rpc/internal/methods/get_fee_stats.go +++ b/cmd/soroban-rpc/internal/methods/get_fee_stats.go @@ -67,7 +67,7 @@ func NewGetFeeStatsHandler(windows *feewindow.FeeWindows, ledgerReader db.Ledger if err != nil { return GetFeeStatsResult{}, jrpc2.Error{ Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), } } diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go index c0e16cb4..845fb7b5 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go @@ -11,6 +11,7 @@ import ( "github.com/stellar/go/xdr" "github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/db" + "github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/ledgerbucketwindow" ) const ( @@ -25,8 +26,10 @@ type ConstantLedgerEntryReaderTx struct{} type ConstantLedgerReader struct{} -func (ledgerReader *ConstantLedgerReader) NewTx(ctx context.Context) (db.LedgerReaderTx, error) { - return nil, nil +type ConstantLedgerReaderTx struct{} + +func (ledgerReader *ConstantLedgerReader) NewTx(_ context.Context) (db.LedgerReaderTx, error) { + return ConstantLedgerReaderTx{}, nil } func (entryReader *ConstantLedgerEntryReader) GetLatestLedgerSequence(_ context.Context) (uint32, error) { @@ -41,6 +44,10 @@ func (entryReader *ConstantLedgerEntryReader) NewCachedTx(_ context.Context) (db return ConstantLedgerEntryReaderTx{}, nil } +func (tx ConstantLedgerReaderTx) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { + return ledgerbucketwindow.LedgerRange{}, nil +} + func (entryReaderTx ConstantLedgerEntryReaderTx) GetLatestLedgerSequence() (uint32, error) { return expectedLatestLedgerSequence, nil } diff --git a/cmd/soroban-rpc/internal/methods/get_transaction.go b/cmd/soroban-rpc/internal/methods/get_transaction.go index 0a89c066..6e13b200 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction.go @@ -96,7 +96,7 @@ func GetTransaction( if err != nil { return GetTransactionResponse{}, jrpc2.Error{ Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), } } diff --git a/cmd/soroban-rpc/internal/methods/get_transactions.go b/cmd/soroban-rpc/internal/methods/get_transactions.go index a18c5ae4..88d0db59 100644 --- a/cmd/soroban-rpc/internal/methods/get_transactions.go +++ b/cmd/soroban-rpc/internal/methods/get_transactions.go @@ -218,7 +218,7 @@ func (h transactionsRPCHandler) getTransactionsByLedgerSequence(ctx context.Cont if err != nil { return GetTransactionsResponse{}, jrpc2.Error{ Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), } } From b4ec665297c18c9f4dc1571306857101b00f4c8f Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Tue, 6 Aug 2024 11:37:34 -0400 Subject: [PATCH 17/37] Fix linting - 2 --- cmd/soroban-rpc/internal/methods/health.go | 2 +- cmd/soroban-rpc/internal/methods/send_transaction.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-rpc/internal/methods/health.go b/cmd/soroban-rpc/internal/methods/health.go index 1006029c..331256f4 100644 --- a/cmd/soroban-rpc/internal/methods/health.go +++ b/cmd/soroban-rpc/internal/methods/health.go @@ -28,7 +28,7 @@ func NewHealthCheck( if err != nil { return HealthCheckResult{}, jrpc2.Error{ Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), } } diff --git a/cmd/soroban-rpc/internal/methods/send_transaction.go b/cmd/soroban-rpc/internal/methods/send_transaction.go index 579b07f2..68669a3d 100644 --- a/cmd/soroban-rpc/internal/methods/send_transaction.go +++ b/cmd/soroban-rpc/internal/methods/send_transaction.go @@ -78,7 +78,7 @@ func NewSendTransactionHandler( if err != nil { return SendTransactionResponse{}, jrpc2.Error{ Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %s", err).Error(), + Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), } } From c07bb2f01c25689518a0c598498f1909bb28b5ad Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Tue, 6 Aug 2024 11:57:09 -0400 Subject: [PATCH 18/37] Reduce the time further --- cmd/soroban-rpc/internal/db/ledger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index 0550ded2..839057fe 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -49,7 +49,7 @@ func (r *ledgerReaderTx) GetLedgerRange(ctx context.Context) (ledgerbucketwindow query := sq.Select("meta"). From(ledgerCloseMetaTableName). Where( - sq.Expr("sequence = (?)", sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName)), + fmt.Sprintf("sequence = (SELECT MIN(sequence) FROM %s)", ledgerCloseMetaTableName), ) var lcm []xdr.LedgerCloseMeta if err := r.db.Select(ctx, &lcm, query); err != nil { From 1dcb54001e2981921a7df9b68a37e8dacc75bc2f Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 12 Aug 2024 12:15:27 -0400 Subject: [PATCH 19/37] Remove creating transaction for GetLedgerRange --- cmd/soroban-rpc/internal/db/db.go | 7 +- cmd/soroban-rpc/internal/db/ledger.go | 115 ++++++++---------- cmd/soroban-rpc/internal/db/ledger_test.go | 25 ++-- cmd/soroban-rpc/internal/db/mocks.go | 18 +-- .../internal/methods/get_fee_stats.go | 11 +- .../methods/get_latest_ledger_test.go | 10 +- .../internal/methods/get_transaction.go | 10 +- .../internal/methods/get_transactions.go | 10 +- cmd/soroban-rpc/internal/methods/health.go | 10 +- .../internal/methods/send_transaction.go | 13 +- 10 files changed, 77 insertions(+), 152 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index 8392db9d..99520c84 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -134,12 +134,7 @@ func getMetaValue(ctx context.Context, q db.SessionInterface, key string) (strin } func getLatestLedgerSequence(ctx context.Context, ledgerReader LedgerReader, cache *dbCache) (uint32, error) { - tx, err := ledgerReader.NewTx(ctx) - if err != nil { - return 0, err - } - - ledgerRange, err := tx.GetLedgerRange(ctx) + ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { return 0, err } diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index 839057fe..d841a04f 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -2,7 +2,6 @@ package db import ( "context" - "database/sql" "fmt" sq "github.com/Masterminds/squirrel" @@ -21,7 +20,7 @@ type StreamLedgerFn func(xdr.LedgerCloseMeta) error type LedgerReader interface { GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error - NewTx(ctx context.Context) (LedgerReaderTx, error) + GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) } type LedgerWriter interface { @@ -29,21 +28,61 @@ type LedgerWriter interface { } type ledgerReader struct { - db *DB + db *DB + latestLedgerSeqCache uint32 + latestLedgerCloseTimeCache int64 } -type LedgerReaderTx interface { - GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) +func NewLedgerReader(db *DB) LedgerReader { + db.cache.RLock() + defer db.cache.RUnlock() + return ledgerReader{ + db: db, + latestLedgerSeqCache: db.cache.latestLedgerSeq, + latestLedgerCloseTimeCache: db.cache.latestLedgerCloseTime, + } } -type ledgerReaderTx struct { - db *DB - latestLedgerSeqCache uint32 - latestLedgerCloseTimeCache int64 +// StreamAllLedgers runs f over all the ledgers in the database (until f errors or signals it's done). +func (r ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error { + sql := sq.Select("meta").From(ledgerCloseMetaTableName).OrderBy("sequence asc") + q, err := r.db.Query(ctx, sql) + if err != nil { + return err + } + defer q.Close() + for q.Next() { + var closeMeta xdr.LedgerCloseMeta + if err = q.Scan(&closeMeta); err != nil { + return err + } + if err = f(closeMeta); err != nil { + return err + } + } + return q.Err() +} + +// GetLedger fetches a single ledger from the db. +func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) { + sql := sq.Select("meta").From(ledgerCloseMetaTableName).Where(sq.Eq{"sequence": sequence}) + var results []xdr.LedgerCloseMeta + if err := r.db.Select(ctx, &results, sql); err != nil { + return xdr.LedgerCloseMeta{}, false, err + } + switch len(results) { + case 0: + return xdr.LedgerCloseMeta{}, false, nil + case 1: + return results[0], true, nil + default: + return xdr.LedgerCloseMeta{}, false, fmt.Errorf("multiple lcm entries (%d) for sequence %d in table %q", + len(results), sequence, ledgerCloseMetaTableName) + } } // GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. -func (r *ledgerReaderTx) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { +func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { // Make use of the cached latest ledger seq and close time to query only the oldest ledger details. if r.latestLedgerSeqCache != 0 { query := sq.Select("meta"). @@ -100,62 +139,6 @@ func (r *ledgerReaderTx) GetLedgerRange(ctx context.Context) (ledgerbucketwindow }, nil } -func NewLedgerReader(db *DB) LedgerReader { - return ledgerReader{db: db} -} - -func (r ledgerReader) NewTx(ctx context.Context) (LedgerReaderTx, error) { - txSession := r.db.Clone() - if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { - return nil, err - } - r.db.cache.RLock() - defer r.db.cache.RUnlock() - return &ledgerReaderTx{ - db: r.db, - latestLedgerSeqCache: r.db.cache.latestLedgerSeq, - latestLedgerCloseTimeCache: r.db.cache.latestLedgerCloseTime, - }, nil -} - -// StreamAllLedgers runs f over all the ledgers in the database (until f errors or signals it's done). -func (r ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error { - sql := sq.Select("meta").From(ledgerCloseMetaTableName).OrderBy("sequence asc") - q, err := r.db.Query(ctx, sql) - if err != nil { - return err - } - defer q.Close() - for q.Next() { - var closeMeta xdr.LedgerCloseMeta - if err = q.Scan(&closeMeta); err != nil { - return err - } - if err = f(closeMeta); err != nil { - return err - } - } - return q.Err() -} - -// GetLedger fetches a single ledger from the db. -func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) { - sql := sq.Select("meta").From(ledgerCloseMetaTableName).Where(sq.Eq{"sequence": sequence}) - var results []xdr.LedgerCloseMeta - if err := r.db.Select(ctx, &results, sql); err != nil { - return xdr.LedgerCloseMeta{}, false, err - } - switch len(results) { - case 0: - return xdr.LedgerCloseMeta{}, false, nil - case 1: - return results[0], true, nil - default: - return xdr.LedgerCloseMeta{}, false, fmt.Errorf("multiple lcm entries (%d) for sequence %d in table %q", - len(results), sequence, ledgerCloseMetaTableName) - } -} - type ledgerWriter struct { stmtCache *sq.StmtCache } diff --git a/cmd/soroban-rpc/internal/db/ledger_test.go b/cmd/soroban-rpc/internal/db/ledger_test.go index 6e86ad61..dd1283c5 100644 --- a/cmd/soroban-rpc/internal/db/ledger_test.go +++ b/cmd/soroban-rpc/internal/db/ledger_test.go @@ -133,10 +133,10 @@ func TestGetLedgerRange_NonEmptyDB(t *testing.T) { require.NoError(t, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) - tx, err := reader.NewTx(ctx) - require.NoError(t, err) + // tx, err := reader.NewTx(ctx) + // require.NoError(t, err) - ledgerRange, err := tx.GetLedgerRange(ctx) + ledgerRange, err := reader.GetLedgerRange(ctx) require.NoError(t, err) assert.Equal(t, uint32(1334), ledgerRange.FirstLedger.Sequence) assert.Equal(t, ledgerCloseTime(1334), ledgerRange.FirstLedger.CloseTime) @@ -164,10 +164,10 @@ func TestGetLedgerRange_SingleDBRow(t *testing.T) { require.NoError(t, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) - tx, err := reader.NewTx(ctx) - require.NoError(t, err) + // tx, err := reader.NewTx(ctx) + // require.NoError(t, err) - ledgerRange, err := tx.GetLedgerRange(ctx) + ledgerRange, err := reader.GetLedgerRange(ctx) require.NoError(t, err) assert.Equal(t, uint32(1334), ledgerRange.FirstLedger.Sequence) assert.Equal(t, ledgerCloseTime(1334), ledgerRange.FirstLedger.CloseTime) @@ -180,10 +180,10 @@ func TestGetLedgerRange_EmptyDB(t *testing.T) { ctx := context.TODO() reader := NewLedgerReader(db) - tx, err := reader.NewTx(ctx) - require.NoError(t, err) + // tx, err := reader.NewTx(ctx) + // require.NoError(t, err) - ledgerRange, err := tx.GetLedgerRange(ctx) + ledgerRange, err := reader.GetLedgerRange(ctx) assert.Equal(t, ErrEmptyDB, err) assert.Equal(t, uint32(0), ledgerRange.FirstLedger.Sequence) assert.Equal(t, int64(0), ledgerRange.FirstLedger.CloseTime) @@ -194,7 +194,6 @@ func TestGetLedgerRange_EmptyDB(t *testing.T) { func BenchmarkGetLedgerRange(b *testing.B) { db := NewTestDB(b) logger := log.DefaultLogger - ctx := context.TODO() writer := NewReadWriter(logger, db, interfaces.MakeNoOpDeamon(), 100, 1_000_000, passphrase) write, err := writer.NewTx(context.TODO()) require.NoError(b, err) @@ -212,12 +211,12 @@ func BenchmarkGetLedgerRange(b *testing.B) { } require.NoError(b, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) - tx, err := reader.NewTx(ctx) - require.NoError(b, err) + // tx, err := reader.NewTx(ctx) + // require.NoError(b, err) b.ResetTimer() for range b.N { - ledgerRange, err := tx.GetLedgerRange(context.TODO()) + ledgerRange, err := reader.GetLedgerRange(context.TODO()) require.NoError(b, err) assert.Equal(b, lcms[0].LedgerSequence(), ledgerRange.FirstLedger.Sequence) assert.Equal(b, lcms[len(lcms)-1].LedgerSequence(), ledgerRange.LastLedger.Sequence) diff --git a/cmd/soroban-rpc/internal/db/mocks.go b/cmd/soroban-rpc/internal/db/mocks.go index 2b001d6a..492d64bb 100644 --- a/cmd/soroban-rpc/internal/db/mocks.go +++ b/cmd/soroban-rpc/internal/db/mocks.go @@ -79,14 +79,6 @@ func (txn *MockTransactionHandler) GetTransaction(_ context.Context, hash xdr.Ha func (txn *MockTransactionHandler) RegisterMetrics(_, _ prometheus.Observer) {} -type MockLedgerReaderTx struct { - reader *MockLedgerReader -} - -func (tx *MockLedgerReaderTx) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { - return tx.reader.txn.ledgerRange, nil -} - type MockLedgerReader struct { txn *MockTransactionHandler } @@ -97,12 +89,6 @@ func NewMockLedgerReader(txn *MockTransactionHandler) *MockLedgerReader { } } -func (m *MockLedgerReader) NewTx(_ context.Context) (LedgerReaderTx, error) { - return &MockLedgerReaderTx{ - reader: m, - }, nil -} - func (m *MockLedgerReader) GetLedger(_ context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) { lcm, ok := m.txn.ledgerSeqToMeta[sequence] if !ok { @@ -115,6 +101,10 @@ func (m *MockLedgerReader) StreamAllLedgers(_ context.Context, _ StreamLedgerFn) return nil } +func (m *MockLedgerReader) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { + return m.txn.ledgerRange, nil +} + var ( _ TransactionReader = &MockTransactionHandler{} _ TransactionWriter = &MockTransactionHandler{} diff --git a/cmd/soroban-rpc/internal/methods/get_fee_stats.go b/cmd/soroban-rpc/internal/methods/get_fee_stats.go index 5d5cf917..9fd85d76 100644 --- a/cmd/soroban-rpc/internal/methods/get_fee_stats.go +++ b/cmd/soroban-rpc/internal/methods/get_fee_stats.go @@ -2,7 +2,6 @@ package methods import ( "context" - "fmt" "github.com/creachadair/jrpc2" @@ -63,15 +62,7 @@ func NewGetFeeStatsHandler(windows *feewindow.FeeWindows, ledgerReader db.Ledger logger *log.Entry, ) jrpc2.Handler { return NewHandler(func(ctx context.Context) (GetFeeStatsResult, error) { - tx, err := ledgerReader.NewTx(ctx) - if err != nil { - return GetFeeStatsResult{}, jrpc2.Error{ - Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), - } - } - - ledgerRange, err := tx.GetLedgerRange(ctx) + ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { // still not fatal logger.WithError(err). Error("could not fetch ledger range") diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go index 845fb7b5..e462892a 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go @@ -26,11 +26,11 @@ type ConstantLedgerEntryReaderTx struct{} type ConstantLedgerReader struct{} -type ConstantLedgerReaderTx struct{} +// type ConstantLedgerReaderTx struct{} -func (ledgerReader *ConstantLedgerReader) NewTx(_ context.Context) (db.LedgerReaderTx, error) { - return ConstantLedgerReaderTx{}, nil -} +//func (ledgerReader *ConstantLedgerReader) NewTx(_ context.Context) (db.LedgerReaderTx, error) { +// return ConstantLedgerReaderTx{}, nil +//} func (entryReader *ConstantLedgerEntryReader) GetLatestLedgerSequence(_ context.Context) (uint32, error) { return expectedLatestLedgerSequence, nil @@ -44,7 +44,7 @@ func (entryReader *ConstantLedgerEntryReader) NewCachedTx(_ context.Context) (db return ConstantLedgerEntryReaderTx{}, nil } -func (tx ConstantLedgerReaderTx) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { +func (entryReader *ConstantLedgerReader) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { return ledgerbucketwindow.LedgerRange{}, nil } diff --git a/cmd/soroban-rpc/internal/methods/get_transaction.go b/cmd/soroban-rpc/internal/methods/get_transaction.go index 6e13b200..ac6ac8b2 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction.go @@ -92,15 +92,7 @@ func GetTransaction( } } - ledgerTx, err := ledgerReader.NewTx(ctx) - if err != nil { - return GetTransactionResponse{}, jrpc2.Error{ - Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), - } - } - - storeRange, err := ledgerTx.GetLedgerRange(ctx) + storeRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { return GetTransactionResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, diff --git a/cmd/soroban-rpc/internal/methods/get_transactions.go b/cmd/soroban-rpc/internal/methods/get_transactions.go index 88d0db59..13ff359f 100644 --- a/cmd/soroban-rpc/internal/methods/get_transactions.go +++ b/cmd/soroban-rpc/internal/methods/get_transactions.go @@ -214,15 +214,7 @@ func (h transactionsRPCHandler) processTransactionsInLedger(ledger xdr.LedgerClo func (h transactionsRPCHandler) getTransactionsByLedgerSequence(ctx context.Context, request GetTransactionsRequest, ) (GetTransactionsResponse, error) { - tx, err := h.ledgerReader.NewTx(ctx) - if err != nil { - return GetTransactionsResponse{}, jrpc2.Error{ - Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), - } - } - - ledgerRange, err := tx.GetLedgerRange(ctx) + ledgerRange, err := h.ledgerReader.GetLedgerRange(ctx) if err != nil { return GetTransactionsResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, diff --git a/cmd/soroban-rpc/internal/methods/health.go b/cmd/soroban-rpc/internal/methods/health.go index 331256f4..8de0767c 100644 --- a/cmd/soroban-rpc/internal/methods/health.go +++ b/cmd/soroban-rpc/internal/methods/health.go @@ -24,15 +24,7 @@ func NewHealthCheck( maxHealthyLedgerLatency time.Duration, ) jrpc2.Handler { return NewHandler(func(ctx context.Context) (HealthCheckResult, error) { - tx, err := ledgerReader.NewTx(ctx) - if err != nil { - return HealthCheckResult{}, jrpc2.Error{ - Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), - } - } - - ledgerRange, err := tx.GetLedgerRange(ctx) + ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil || ledgerRange.LastLedger.Sequence < 1 { extra := "" if err != nil { diff --git a/cmd/soroban-rpc/internal/methods/send_transaction.go b/cmd/soroban-rpc/internal/methods/send_transaction.go index 68669a3d..9cdd6a20 100644 --- a/cmd/soroban-rpc/internal/methods/send_transaction.go +++ b/cmd/soroban-rpc/internal/methods/send_transaction.go @@ -3,7 +3,6 @@ package methods import ( "context" "encoding/hex" - "fmt" "github.com/creachadair/jrpc2" @@ -74,21 +73,13 @@ func NewSendTransactionHandler( } txHash := hex.EncodeToString(hash[:]) - tx, err := ledgerReader.NewTx(ctx) - if err != nil { - return SendTransactionResponse{}, jrpc2.Error{ - Code: jrpc2.InternalError, - Message: fmt.Errorf("could not initialize ledger reader tx: %w", err).Error(), - } - } - - ledgerRange, err := tx.GetLedgerRange(ctx) + ledgerInfo, err := ledgerReader.GetLedgerRange(ctx) if err != nil { // still not fatal logger.WithError(err). WithField("tx", request.Transaction). Error("could not fetch ledger range") } - latestLedgerInfo := ledgerRange.LastLedger + latestLedgerInfo := ledgerInfo.LastLedger resp, err := submitter.SubmitTransaction(ctx, request.Transaction) if err != nil { From 07a753b74ed0df5fb59ceab3194f03486967d4bf Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 12 Aug 2024 12:17:05 -0400 Subject: [PATCH 20/37] Remove creating transaction for GetLedgerRange - 2 --- cmd/soroban-rpc/internal/db/ledger_test.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledger_test.go b/cmd/soroban-rpc/internal/db/ledger_test.go index dd1283c5..1b8b2fc0 100644 --- a/cmd/soroban-rpc/internal/db/ledger_test.go +++ b/cmd/soroban-rpc/internal/db/ledger_test.go @@ -133,9 +133,6 @@ func TestGetLedgerRange_NonEmptyDB(t *testing.T) { require.NoError(t, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) - // tx, err := reader.NewTx(ctx) - // require.NoError(t, err) - ledgerRange, err := reader.GetLedgerRange(ctx) require.NoError(t, err) assert.Equal(t, uint32(1334), ledgerRange.FirstLedger.Sequence) @@ -164,9 +161,6 @@ func TestGetLedgerRange_SingleDBRow(t *testing.T) { require.NoError(t, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) - // tx, err := reader.NewTx(ctx) - // require.NoError(t, err) - ledgerRange, err := reader.GetLedgerRange(ctx) require.NoError(t, err) assert.Equal(t, uint32(1334), ledgerRange.FirstLedger.Sequence) @@ -180,9 +174,6 @@ func TestGetLedgerRange_EmptyDB(t *testing.T) { ctx := context.TODO() reader := NewLedgerReader(db) - // tx, err := reader.NewTx(ctx) - // require.NoError(t, err) - ledgerRange, err := reader.GetLedgerRange(ctx) assert.Equal(t, ErrEmptyDB, err) assert.Equal(t, uint32(0), ledgerRange.FirstLedger.Sequence) @@ -211,8 +202,6 @@ func BenchmarkGetLedgerRange(b *testing.B) { } require.NoError(b, write.Commit(lcms[len(lcms)-1])) reader := NewLedgerReader(db) - // tx, err := reader.NewTx(ctx) - // require.NoError(b, err) b.ResetTimer() for range b.N { From c7ce36b72afbd25f662815d575ab12ddeaa44340 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 12 Aug 2024 12:58:33 -0400 Subject: [PATCH 21/37] Remove creating transaction for GetLedgerRange - 3 --- .../internal/methods/get_latest_ledger_test.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go index e462892a..affa0536 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go @@ -26,11 +26,9 @@ type ConstantLedgerEntryReaderTx struct{} type ConstantLedgerReader struct{} -// type ConstantLedgerReaderTx struct{} - -//func (ledgerReader *ConstantLedgerReader) NewTx(_ context.Context) (db.LedgerReaderTx, error) { -// return ConstantLedgerReaderTx{}, nil -//} +func (ledgerReader *ConstantLedgerReader) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { + return ledgerbucketwindow.LedgerRange{}, nil +} func (entryReader *ConstantLedgerEntryReader) GetLatestLedgerSequence(_ context.Context) (uint32, error) { return expectedLatestLedgerSequence, nil @@ -44,10 +42,6 @@ func (entryReader *ConstantLedgerEntryReader) NewCachedTx(_ context.Context) (db return ConstantLedgerEntryReaderTx{}, nil } -func (entryReader *ConstantLedgerReader) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) { - return ledgerbucketwindow.LedgerRange{}, nil -} - func (entryReaderTx ConstantLedgerEntryReaderTx) GetLatestLedgerSequence() (uint32, error) { return expectedLatestLedgerSequence, nil } @@ -97,5 +91,3 @@ func TestGetLatestLedger(t *testing.T) { assert.Equal(t, expectedLatestLedgerProtocolVersion, latestLedgerResp.ProtocolVersion) assert.Equal(t, expectedLatestLedgerSequence, latestLedgerResp.Sequence) } - -var _ db.LedgerReader = &ConstantLedgerReader{} From 6f0b8dd6c55599c9b29bc3b1680b8bee7ceab09b Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 12 Aug 2024 13:38:56 -0400 Subject: [PATCH 22/37] Fix failing unittest - 1 --- cmd/soroban-rpc/internal/db/ledger.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index d841a04f..ed30a9ba 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -36,7 +36,7 @@ type ledgerReader struct { func NewLedgerReader(db *DB) LedgerReader { db.cache.RLock() defer db.cache.RUnlock() - return ledgerReader{ + return &ledgerReader{ db: db, latestLedgerSeqCache: db.cache.latestLedgerSeq, latestLedgerCloseTimeCache: db.cache.latestLedgerCloseTime, @@ -44,7 +44,7 @@ func NewLedgerReader(db *DB) LedgerReader { } // StreamAllLedgers runs f over all the ledgers in the database (until f errors or signals it's done). -func (r ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error { +func (r *ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error { sql := sq.Select("meta").From(ledgerCloseMetaTableName).OrderBy("sequence asc") q, err := r.db.Query(ctx, sql) if err != nil { @@ -64,7 +64,7 @@ func (r ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) er } // GetLedger fetches a single ledger from the db. -func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) { +func (r *ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) { sql := sq.Select("meta").From(ledgerCloseMetaTableName).Where(sq.Eq{"sequence": sequence}) var results []xdr.LedgerCloseMeta if err := r.db.Select(ctx, &results, sql); err != nil { @@ -82,7 +82,7 @@ func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.Ledge } // GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. -func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { +func (r *ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { // Make use of the cached latest ledger seq and close time to query only the oldest ledger details. if r.latestLedgerSeqCache != 0 { query := sq.Select("meta"). From ec749d0b3b93ed06e618db8925e6d5330920fe4c Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 12 Aug 2024 14:00:29 -0400 Subject: [PATCH 23/37] Fix failing tests - 2 --- cmd/soroban-rpc/internal/db/ledger.go | 35 ++++++++++++--------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index ed30a9ba..d6d4039e 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -28,23 +28,15 @@ type LedgerWriter interface { } type ledgerReader struct { - db *DB - latestLedgerSeqCache uint32 - latestLedgerCloseTimeCache int64 + db *DB } func NewLedgerReader(db *DB) LedgerReader { - db.cache.RLock() - defer db.cache.RUnlock() - return &ledgerReader{ - db: db, - latestLedgerSeqCache: db.cache.latestLedgerSeq, - latestLedgerCloseTimeCache: db.cache.latestLedgerCloseTime, - } + return ledgerReader{db: db} } // StreamAllLedgers runs f over all the ledgers in the database (until f errors or signals it's done). -func (r *ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error { +func (r ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error { sql := sq.Select("meta").From(ledgerCloseMetaTableName).OrderBy("sequence asc") q, err := r.db.Query(ctx, sql) if err != nil { @@ -64,7 +56,7 @@ func (r *ledgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) e } // GetLedger fetches a single ledger from the db. -func (r *ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) { +func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) { sql := sq.Select("meta").From(ledgerCloseMetaTableName).Where(sq.Eq{"sequence": sequence}) var results []xdr.LedgerCloseMeta if err := r.db.Select(ctx, &results, sql); err != nil { @@ -82,9 +74,17 @@ func (r *ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.Ledg } // GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. -func (r *ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { +func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { + var latestLedgerSeqCache uint32 + var latestLedgerCloseTimeCache int64 + + r.db.cache.RLock() + latestLedgerSeqCache = r.db.cache.latestLedgerSeq + latestLedgerCloseTimeCache = r.db.cache.latestLedgerCloseTime + r.db.cache.RUnlock() + // Make use of the cached latest ledger seq and close time to query only the oldest ledger details. - if r.latestLedgerSeqCache != 0 { + if latestLedgerSeqCache != 0 { query := sq.Select("meta"). From(ledgerCloseMetaTableName). Where( @@ -101,8 +101,8 @@ func (r *ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.L CloseTime: lcm[0].LedgerCloseTime(), }, LastLedger: ledgerbucketwindow.LedgerInfo{ - Sequence: r.latestLedgerSeqCache, - CloseTime: r.latestLedgerCloseTimeCache, + Sequence: latestLedgerSeqCache, + CloseTime: latestLedgerCloseTimeCache, }, }, nil } @@ -124,9 +124,6 @@ func (r *ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.L return ledgerbucketwindow.LedgerRange{}, ErrEmptyDB } - r.latestLedgerSeqCache = lcms[len(lcms)-1].LedgerSequence() - r.latestLedgerCloseTimeCache = lcms[len(lcms)-1].LedgerCloseTime() - return ledgerbucketwindow.LedgerRange{ FirstLedger: ledgerbucketwindow.LedgerInfo{ Sequence: lcms[0].LedgerSequence(), From 9229df43f5a2e23823d44fce09a3ab418e1947ab Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 12 Aug 2024 14:55:43 -0400 Subject: [PATCH 24/37] Update cmd/soroban-rpc/internal/db/ledger.go Co-authored-by: tamirms --- cmd/soroban-rpc/internal/db/ledger.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index d6d4039e..1aaf59ba 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -75,12 +75,9 @@ func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.Ledge // GetLedgerRange pulls the min/max ledger sequence numbers from the meta table. func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) { - var latestLedgerSeqCache uint32 - var latestLedgerCloseTimeCache int64 - r.db.cache.RLock() - latestLedgerSeqCache = r.db.cache.latestLedgerSeq - latestLedgerCloseTimeCache = r.db.cache.latestLedgerCloseTime + latestLedgerSeqCache := r.db.cache.latestLedgerSeq + latestLedgerCloseTimeCache := r.db.cache.latestLedgerCloseTime r.db.cache.RUnlock() // Make use of the cached latest ledger seq and close time to query only the oldest ledger details. From 4696a6b7426aa46a011250a6e497c0cbed9feef4 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 12 Aug 2024 17:01:41 -0400 Subject: [PATCH 25/37] Add check for empty result in ledger range --- cmd/soroban-rpc/internal/db/ledger.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/db/ledger.go b/cmd/soroban-rpc/internal/db/ledger.go index 1aaf59ba..4f01f119 100644 --- a/cmd/soroban-rpc/internal/db/ledger.go +++ b/cmd/soroban-rpc/internal/db/ledger.go @@ -92,6 +92,10 @@ func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.Le return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err) } + if len(lcm) == 0 { + return ledgerbucketwindow.LedgerRange{}, ErrEmptyDB + } + return ledgerbucketwindow.LedgerRange{ FirstLedger: ledgerbucketwindow.LedgerInfo{ Sequence: lcm[0].LedgerSequence(), @@ -116,7 +120,6 @@ func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.Le return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err) } - // Empty DB if len(lcms) == 0 { return ledgerbucketwindow.LedgerRange{}, ErrEmptyDB } From 19b398923132cc5b726c5adf1b0065b3f2792d30 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Tue, 13 Aug 2024 13:41:38 -0400 Subject: [PATCH 26/37] Add a cache check on GetLatestLedgerSequence call --- cmd/soroban-rpc/internal/db/ledgerentry.go | 7 +++++++ .../internal/methods/get_latest_ledger.go | 13 +------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 2e742752..6ee219f0 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -342,6 +342,13 @@ func NewLedgerEntryReader(db *DB) LedgerEntryReader { } func (r ledgerEntryReader) GetLatestLedgerSequence(ctx context.Context) (uint32, error) { + r.db.cache.RLock() + latestLedgerSeqCache := r.db.cache.latestLedgerSeq + r.db.cache.RUnlock() + + if latestLedgerSeqCache != 0 { + return latestLedgerSeqCache, nil + } return getLatestLedgerSequence(ctx, NewLedgerReader(r.db), r.db.cache) } diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger.go index 3617e70d..c330adb4 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger.go @@ -20,18 +20,7 @@ type GetLatestLedgerResponse struct { // NewGetLatestLedgerHandler returns a JSON RPC handler to retrieve the latest ledger entry from Stellar core. func NewGetLatestLedgerHandler(ledgerEntryReader db.LedgerEntryReader, ledgerReader db.LedgerReader) jrpc2.Handler { return NewHandler(func(ctx context.Context) (GetLatestLedgerResponse, error) { - tx, err := ledgerEntryReader.NewTx(ctx) - if err != nil { - return GetLatestLedgerResponse{}, &jrpc2.Error{ - Code: jrpc2.InternalError, - Message: "could not create read transaction", - } - } - defer func() { - _ = tx.Done() - }() - - latestSequence, err := tx.GetLatestLedgerSequence() + latestSequence, err := ledgerEntryReader.GetLatestLedgerSequence(ctx) if err != nil { return GetLatestLedgerResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, From 288e446f998d59fe01d7aba87e60970ed5a2028a Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Tue, 13 Aug 2024 15:38:10 -0400 Subject: [PATCH 27/37] Move cache read to DB function --- cmd/soroban-rpc/internal/db/db.go | 8 ++++++++ cmd/soroban-rpc/internal/db/ledgerentry.go | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index 99520c84..e7439785 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -134,6 +134,14 @@ func getMetaValue(ctx context.Context, q db.SessionInterface, key string) (strin } func getLatestLedgerSequence(ctx context.Context, ledgerReader LedgerReader, cache *dbCache) (uint32, error) { + cache.RLock() + latestLedgerSeqCache := cache.latestLedgerSeq + cache.RUnlock() + + if latestLedgerSeqCache != 0 { + return latestLedgerSeqCache, nil + } + ledgerRange, err := ledgerReader.GetLedgerRange(ctx) if err != nil { return 0, err diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 6ee219f0..2e742752 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -342,13 +342,6 @@ func NewLedgerEntryReader(db *DB) LedgerEntryReader { } func (r ledgerEntryReader) GetLatestLedgerSequence(ctx context.Context) (uint32, error) { - r.db.cache.RLock() - latestLedgerSeqCache := r.db.cache.latestLedgerSeq - r.db.cache.RUnlock() - - if latestLedgerSeqCache != 0 { - return latestLedgerSeqCache, nil - } return getLatestLedgerSequence(ctx, NewLedgerReader(r.db), r.db.cache) } From f7ebe3ebccb47d02edc85946e63544282af6bb10 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Tue, 13 Aug 2024 15:46:16 -0400 Subject: [PATCH 28/37] Move cache read to DB function - 2 --- cmd/soroban-rpc/internal/db/ledgerentry.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 2e742752..782d244a 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -146,9 +146,6 @@ type ledgerEntryReadTx struct { } func (l *ledgerEntryReadTx) GetLatestLedgerSequence() (uint32, error) { - if l.latestLedgerSeqCache != 0 { - return l.latestLedgerSeqCache, nil - } latestLedgerSeq, err := getLatestLedgerSequence(context.Background(), l.ledgerReader, l.globalCache) if err == nil { l.latestLedgerSeqCache = latestLedgerSeq From bc07626c798661e985eff2b98b880b9f7424eead Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Tue, 13 Aug 2024 16:59:54 -0400 Subject: [PATCH 29/37] Revert "Move cache read to DB function - 2" This reverts commit f7ebe3ebccb47d02edc85946e63544282af6bb10. --- cmd/soroban-rpc/internal/db/ledgerentry.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 782d244a..2e742752 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -146,6 +146,9 @@ type ledgerEntryReadTx struct { } func (l *ledgerEntryReadTx) GetLatestLedgerSequence() (uint32, error) { + if l.latestLedgerSeqCache != 0 { + return l.latestLedgerSeqCache, nil + } latestLedgerSeq, err := getLatestLedgerSequence(context.Background(), l.ledgerReader, l.globalCache) if err == nil { l.latestLedgerSeqCache = latestLedgerSeq From 3d8f4b86cb7266413649951416590b78a034f4dd Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 26 Aug 2024 17:23:22 -0400 Subject: [PATCH 30/37] Lock cache before creating tx --- cmd/soroban-rpc/internal/db/ledgerentry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 2e742752..9cf13f19 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -373,10 +373,10 @@ func (r ledgerEntryReader) NewCachedTx(ctx context.Context) (LedgerEntryReadTx, func (r ledgerEntryReader) NewTx(ctx context.Context) (LedgerEntryReadTx, error) { txSession := r.db.Clone() + r.db.cache.RLock() if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { return nil, err } - r.db.cache.RLock() defer r.db.cache.RUnlock() return &ledgerEntryReadTx{ globalCache: r.db.cache, From 456e2e15087639fd28adeff52e99d299e86b3567 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 26 Aug 2024 17:44:47 -0400 Subject: [PATCH 31/37] Fix failing test --- cmd/soroban-rpc/internal/ingest/service_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cmd/soroban-rpc/internal/ingest/service_test.go b/cmd/soroban-rpc/internal/ingest/service_test.go index 5b79da24..92b7753b 100644 --- a/cmd/soroban-rpc/internal/ingest/service_test.go +++ b/cmd/soroban-rpc/internal/ingest/service_test.go @@ -80,13 +80,6 @@ func TestIngestion(t *testing.T) { mockTxWriter := &MockTransactionWriter{} mockEventWriter := &MockEventWriter{} ctx := context.Background() - mockDB.On("NewTx", ctx).Return(mockTx, nil).Once() - mockTx.On("Commit", sequence).Return(nil).Once() - mockTx.On("Rollback").Return(nil).Once() - mockTx.On("LedgerEntryWriter").Return(mockLedgerEntryWriter).Twice() - mockTx.On("LedgerWriter").Return(mockLedgerWriter).Once() - mockTx.On("TransactionWriter").Return(mockTxWriter).Once() - mockTx.On("EventWriter").Return(mockEventWriter).Once() src := xdr.MustAddress("GBXGQJWVLWOYHFLVTKWV5FGHA3LNYY2JQKM7OAJAUEQFU6LPCSEFVXON") firstTx := xdr.TransactionEnvelope{ @@ -248,6 +241,7 @@ func TestIngestion(t *testing.T) { mockTx.On("LedgerEntryWriter").Return(mockLedgerEntryWriter).Twice() mockTx.On("LedgerWriter").Return(mockLedgerWriter).Once() mockTx.On("TransactionWriter").Return(mockTxWriter).Once() + mockTx.On("EventWriter").Return(mockEventWriter).Once() mockLedgerBackend.On("GetLedger", ctx, sequence).Return(ledger, nil).Once() mockLedgerEntryWriter.On("UpsertLedgerEntry", operationChanges[1].MustUpdated()). From 9ff55a65ad35db08787cd3894d9d1eb5f2677c75 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Mon, 26 Aug 2024 17:52:51 -0400 Subject: [PATCH 32/37] Add migration --- cmd/soroban-rpc/internal/db/sqlmigrations/04_metadata.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 cmd/soroban-rpc/internal/db/sqlmigrations/04_metadata.sql diff --git a/cmd/soroban-rpc/internal/db/sqlmigrations/04_metadata.sql b/cmd/soroban-rpc/internal/db/sqlmigrations/04_metadata.sql new file mode 100644 index 00000000..44c87057 --- /dev/null +++ b/cmd/soroban-rpc/internal/db/sqlmigrations/04_metadata.sql @@ -0,0 +1,5 @@ +-- +migrate Up +DELETE FROM metadata WHERE key = 'LatestLedgerSequence'; + +-- +migrate Down +INSERT INTO metadata (key, value) VALUES ('LatestLedgerSequence', '0'); From 5c3a4e9a05b57c9a9ccfea6337d932745de2c99c Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Tue, 27 Aug 2024 11:11:31 -0400 Subject: [PATCH 33/37] Fix failing test --- .../internal/methods/get_events_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/soroban-rpc/internal/methods/get_events_test.go b/cmd/soroban-rpc/internal/methods/get_events_test.go index 82cf8cf0..44692304 100644 --- a/cmd/soroban-rpc/internal/methods/get_events_test.go +++ b/cmd/soroban-rpc/internal/methods/get_events_test.go @@ -563,7 +563,7 @@ func TestGetEvents(t *testing.T) { ledgerCloseMeta := ledgerCloseMetaWithEvents(2, now.Unix(), txMeta...) require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") assert.NoError(t, eventW.InsertEvents(ledgerCloseMeta)) - require.NoError(t, write.Commit(2)) + require.NoError(t, write.Commit(ledgerCloseMeta)) handler := eventsRPCHandler{ dbReader: store, @@ -616,7 +616,7 @@ func TestGetEvents(t *testing.T) { ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") assert.NoError(t, eventW.InsertEvents(ledgerCloseMeta)) - require.NoError(t, write.Commit(1)) + require.NoError(t, write.Commit(ledgerCloseMeta)) handler := eventsRPCHandler{ dbReader: store, @@ -695,7 +695,7 @@ func TestGetEvents(t *testing.T) { ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events ") - require.NoError(t, write.Commit(2)) + require.NoError(t, write.Commit(ledgerCloseMeta)) handler := eventsRPCHandler{ dbReader: store, @@ -757,7 +757,7 @@ func TestGetEvents(t *testing.T) { require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events ") - require.NoError(t, write.Commit(1)) + require.NoError(t, write.Commit(ledgerCloseMeta)) number := xdr.Uint64(4) handler := eventsRPCHandler{ @@ -902,7 +902,7 @@ func TestGetEvents(t *testing.T) { require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events ") - require.NoError(t, write.Commit(1)) + require.NoError(t, write.Commit(ledgerCloseMeta)) handler := eventsRPCHandler{ dbReader: store, @@ -990,7 +990,7 @@ func TestGetEvents(t *testing.T) { ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events ") - require.NoError(t, write.Commit(1)) + require.NoError(t, write.Commit(ledgerCloseMeta)) handler := eventsRPCHandler{ dbReader: store, @@ -1054,7 +1054,7 @@ func TestGetEvents(t *testing.T) { ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events ") - require.NoError(t, write.Commit(1)) + require.NoError(t, write.Commit(ledgerCloseMeta)) handler := eventsRPCHandler{ dbReader: store, @@ -1153,7 +1153,7 @@ func TestGetEvents(t *testing.T) { ledgerCloseMeta := ledgerCloseMetaWithEvents(5, now.Unix(), txMeta...) require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events ") - require.NoError(t, write.Commit(4)) + require.NoError(t, write.Commit(ledgerCloseMeta)) id := &db.Cursor{Ledger: 5, Tx: 1, Op: 0, Event: 0} handler := eventsRPCHandler{ @@ -1230,8 +1230,8 @@ func BenchmarkGetEvents(b *testing.B) { ledgerCloseMeta := ledgerCloseMetaWithEvents(uint32(i), now.Unix(), txMeta...) require.NoError(b, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger ") require.NoError(b, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events ") + require.NoError(b, write.Commit(ledgerCloseMeta)) } - require.NoError(b, write.Commit(1)) handler := eventsRPCHandler{ dbReader: store, From a437b0ec07716d48f3ca4f60fdf005fa9a20430f Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Wed, 28 Aug 2024 17:41:33 -0400 Subject: [PATCH 34/37] Refactor NewTx and NewCachedTx --- cmd/soroban-rpc/internal/db/ledgerentry.go | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 9cf13f19..95af7388 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -8,7 +8,6 @@ import ( "fmt" sq "github.com/Masterminds/squirrel" - "github.com/stellar/go/support/db" "github.com/stellar/go/xdr" ) @@ -345,18 +344,26 @@ func (r ledgerEntryReader) GetLatestLedgerSequence(ctx context.Context) (uint32, return getLatestLedgerSequence(ctx, NewLedgerReader(r.db), r.db.cache) } +func (r ledgerEntryReader) newBaseTx(ctx context.Context) (db.SessionInterface, error) { + txSession := r.db.Clone() + r.db.cache.RLock() + defer r.db.cache.RUnlock() + if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { + return nil, err + } + return txSession, nil +} + // NewCachedTx() caches all accessed ledger entries and select statements. If many ledger entries are accessed, it will grow without bounds. func (r ledgerEntryReader) NewCachedTx(ctx context.Context) (LedgerEntryReadTx, error) { - txSession := r.db.Clone() // We need to copy the cached ledger entries locally when we start the transaction // since otherwise we would break the consistency between the transaction and the cache. // We need to make the parent cache access atomic with the read transaction creation. // Otherwise, the cache can be made inconsistent if a write transaction finishes // in between, updating the cache. - r.db.cache.RLock() - defer r.db.cache.RUnlock() - if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { + txSession, err := r.newBaseTx(ctx) + if err != nil { return nil, err } cacheReadTx := r.db.cache.ledgerEntries.newReadTx() @@ -372,12 +379,10 @@ func (r ledgerEntryReader) NewCachedTx(ctx context.Context) (LedgerEntryReadTx, } func (r ledgerEntryReader) NewTx(ctx context.Context) (LedgerEntryReadTx, error) { - txSession := r.db.Clone() - r.db.cache.RLock() - if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { + txSession, err := r.newBaseTx(ctx) + if err != nil { return nil, err } - defer r.db.cache.RUnlock() return &ledgerEntryReadTx{ globalCache: r.db.cache, latestLedgerSeqCache: r.db.cache.latestLedgerSeq, From 3957352259a40d76f5da29d3b847dd6d2709eb24 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Wed, 28 Aug 2024 17:55:04 -0400 Subject: [PATCH 35/37] Refactor NewTx and NewCachedTx - 2 --- cmd/soroban-rpc/internal/db/ledgerentry.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 95af7388..64a63f44 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -346,8 +346,6 @@ func (r ledgerEntryReader) GetLatestLedgerSequence(ctx context.Context) (uint32, func (r ledgerEntryReader) newBaseTx(ctx context.Context) (db.SessionInterface, error) { txSession := r.db.Clone() - r.db.cache.RLock() - defer r.db.cache.RUnlock() if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { return nil, err } @@ -362,6 +360,8 @@ func (r ledgerEntryReader) NewCachedTx(ctx context.Context) (LedgerEntryReadTx, // We need to make the parent cache access atomic with the read transaction creation. // Otherwise, the cache can be made inconsistent if a write transaction finishes // in between, updating the cache. + r.db.cache.RLock() + defer r.db.cache.RUnlock() txSession, err := r.newBaseTx(ctx) if err != nil { return nil, err @@ -379,6 +379,8 @@ func (r ledgerEntryReader) NewCachedTx(ctx context.Context) (LedgerEntryReadTx, } func (r ledgerEntryReader) NewTx(ctx context.Context) (LedgerEntryReadTx, error) { + r.db.cache.RLock() + defer r.db.cache.RUnlock() txSession, err := r.newBaseTx(ctx) if err != nil { return nil, err From 0ec8237d15bb94772eca1a27240845127286d18c Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Wed, 28 Aug 2024 17:55:40 -0400 Subject: [PATCH 36/37] Refactor NewTx and NewCachedTx - 3 --- cmd/soroban-rpc/internal/db/ledgerentry.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 64a63f44..13311dca 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -345,11 +345,11 @@ func (r ledgerEntryReader) GetLatestLedgerSequence(ctx context.Context) (uint32, } func (r ledgerEntryReader) newBaseTx(ctx context.Context) (db.SessionInterface, error) { - txSession := r.db.Clone() - if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { + tx := r.db.Clone() + if err := tx.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { return nil, err } - return txSession, nil + return tx, nil } // NewCachedTx() caches all accessed ledger entries and select statements. If many ledger entries are accessed, it will grow without bounds. From ccd80f1cef7a3b8f6201faf75e2e0037e1cc6b3d Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Wed, 4 Sep 2024 17:22:37 -0400 Subject: [PATCH 37/37] Refactor NewTx and NewCachedTx - 4 --- cmd/soroban-rpc/internal/db/ledgerentry.go | 61 +++++++------------ .../internal/db/ledgerentry_test.go | 16 ++--- .../methods/get_latest_ledger_test.go | 6 +- .../internal/methods/get_ledger_entries.go | 2 +- .../internal/methods/get_ledger_entry.go | 2 +- .../internal/methods/simulate_transaction.go | 2 +- .../internal/preflight/preflight_test.go | 4 +- 7 files changed, 35 insertions(+), 58 deletions(-) diff --git a/cmd/soroban-rpc/internal/db/ledgerentry.go b/cmd/soroban-rpc/internal/db/ledgerentry.go index 13311dca..ef704c0d 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry.go @@ -18,8 +18,7 @@ const ( type LedgerEntryReader interface { GetLatestLedgerSequence(ctx context.Context) (uint32, error) - NewTx(ctx context.Context) (LedgerEntryReadTx, error) - NewCachedTx(ctx context.Context) (LedgerEntryReadTx, error) + NewTx(ctx context.Context, cacheTx bool) (LedgerEntryReadTx, error) } type LedgerKeyAndEntry struct { @@ -344,54 +343,36 @@ func (r ledgerEntryReader) GetLatestLedgerSequence(ctx context.Context) (uint32, return getLatestLedgerSequence(ctx, NewLedgerReader(r.db), r.db.cache) } -func (r ledgerEntryReader) newBaseTx(ctx context.Context) (db.SessionInterface, error) { - tx := r.db.Clone() - if err := tx.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { - return nil, err +// NewTx creates a new ledger entry read transaction. When cacheTx is set to True, it will cache all accessed +// ledger entries and select statements. If many ledger entries are accessed, it will grow without bounds. +func (r ledgerEntryReader) NewTx(ctx context.Context, cacheTx bool) (LedgerEntryReadTx, error) { + r.db.cache.RLock() + defer r.db.cache.RUnlock() + txSession := r.db.Clone() + if err := txSession.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}); err != nil { + return nil, fmt.Errorf("failed to begin read transaction: %w", err) + } + tx := &ledgerEntryReadTx{ + globalCache: r.db.cache, + latestLedgerSeqCache: r.db.cache.latestLedgerSeq, + tx: txSession, + ledgerReader: NewLedgerReader(r.db), + buffer: xdr.NewEncodingBuffer(), } - return tx, nil -} -// NewCachedTx() caches all accessed ledger entries and select statements. If many ledger entries are accessed, it will grow without bounds. -func (r ledgerEntryReader) NewCachedTx(ctx context.Context) (LedgerEntryReadTx, error) { // We need to copy the cached ledger entries locally when we start the transaction // since otherwise we would break the consistency between the transaction and the cache. // We need to make the parent cache access atomic with the read transaction creation. // Otherwise, the cache can be made inconsistent if a write transaction finishes // in between, updating the cache. - r.db.cache.RLock() - defer r.db.cache.RUnlock() - txSession, err := r.newBaseTx(ctx) - if err != nil { - return nil, err + if cacheTx { + tx.stmtCache = sq.NewStmtCache(txSession.GetTx()) + cacheReadTx := r.db.cache.ledgerEntries.newReadTx() + tx.ledgerEntryCacheReadTx = &cacheReadTx } - cacheReadTx := r.db.cache.ledgerEntries.newReadTx() - return &ledgerEntryReadTx{ - globalCache: r.db.cache, - stmtCache: sq.NewStmtCache(txSession.GetTx()), - latestLedgerSeqCache: r.db.cache.latestLedgerSeq, - ledgerEntryCacheReadTx: &cacheReadTx, - ledgerReader: NewLedgerReader(r.db), - tx: txSession, - buffer: xdr.NewEncodingBuffer(), - }, nil -} -func (r ledgerEntryReader) NewTx(ctx context.Context) (LedgerEntryReadTx, error) { - r.db.cache.RLock() - defer r.db.cache.RUnlock() - txSession, err := r.newBaseTx(ctx) - if err != nil { - return nil, err - } - return &ledgerEntryReadTx{ - globalCache: r.db.cache, - latestLedgerSeqCache: r.db.cache.latestLedgerSeq, - tx: txSession, - ledgerReader: NewLedgerReader(r.db), - buffer: xdr.NewEncodingBuffer(), - }, nil + return tx, nil } func encodeLedgerKey(buffer *xdr.EncodingBuffer, key xdr.LedgerKey) (string, error) { diff --git a/cmd/soroban-rpc/internal/db/ledgerentry_test.go b/cmd/soroban-rpc/internal/db/ledgerentry_test.go index d898e2ef..d17c560f 100644 --- a/cmd/soroban-rpc/internal/db/ledgerentry_test.go +++ b/cmd/soroban-rpc/internal/db/ledgerentry_test.go @@ -18,7 +18,7 @@ import ( ) func getLedgerEntryAndLatestLedgerSequenceWithErr(db *DB, key xdr.LedgerKey) (bool, xdr.LedgerEntry, uint32, *uint32, error) { - tx, err := NewLedgerEntryReader(db).NewTx(context.Background()) + tx, err := NewLedgerEntryReader(db).NewTx(context.Background(), false) if err != nil { return false, xdr.LedgerEntry{}, 0, nil, err } @@ -256,9 +256,9 @@ func TestReadTxsDuringWriteTx(t *testing.T) { assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) // Before committing the changes, make sure multiple concurrent transactions can query the DB - readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background()) + readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background(), false) assert.NoError(t, err) - readTx2, err := NewLedgerEntryReader(db).NewTx(context.Background()) + readTx2, err := NewLedgerEntryReader(db).NewTx(context.Background(), false) assert.NoError(t, err) _, err = readTx1.GetLatestLedgerSequence() @@ -306,7 +306,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { // Create a multiple read transactions, interleaved with the writing process // First read transaction, before the write transaction is created - readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background()) + readTx1, err := NewLedgerEntryReader(db).NewTx(context.Background(), false) assert.NoError(t, err) // Start filling the DB with a single entry (enforce flushing right away) @@ -315,7 +315,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { writer := tx.LedgerEntryWriter() // Second read transaction, after the write transaction is created - readTx2, err := NewLedgerEntryReader(db).NewTx(context.Background()) + readTx2, err := NewLedgerEntryReader(db).NewTx(context.Background(), false) assert.NoError(t, err) four := xdr.Uint32(4) @@ -344,7 +344,7 @@ func TestWriteTxsDuringReadTxs(t *testing.T) { assert.NoError(t, writer.UpsertLedgerEntry(expLegerEntry)) // Third read transaction, after the first insert has happened in the write transaction - readTx3, err := NewLedgerEntryReader(db).NewTx(context.Background()) + readTx3, err := NewLedgerEntryReader(db).NewTx(context.Background(), false) assert.NoError(t, err) // Make sure that all the read transactions get an emptyDB error before and after the write transaction is committed @@ -532,9 +532,9 @@ func benchmarkLedgerEntry(b *testing.B, cached bool) { var readTx LedgerEntryReadTx var err error if cached { - readTx, err = reader.NewCachedTx(context.Background()) + readTx, err = reader.NewTx(context.Background(), true) } else { - readTx, err = reader.NewTx(context.Background()) + readTx, err = reader.NewTx(context.Background(), false) } assert.NoError(b, err) for i := 0; i < numQueriesPerOp; i++ { diff --git a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go index c86e9d9a..0fcb4494 100644 --- a/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go +++ b/cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go @@ -34,11 +34,7 @@ func (entryReader *ConstantLedgerEntryReader) GetLatestLedgerSequence(_ context. return expectedLatestLedgerSequence, nil } -func (entryReader *ConstantLedgerEntryReader) NewTx(_ context.Context) (db.LedgerEntryReadTx, error) { - return ConstantLedgerEntryReaderTx{}, nil -} - -func (entryReader *ConstantLedgerEntryReader) NewCachedTx(_ context.Context) (db.LedgerEntryReadTx, error) { +func (entryReader *ConstantLedgerEntryReader) NewTx(_ context.Context, _ bool) (db.LedgerEntryReadTx, error) { return ConstantLedgerEntryReaderTx{}, nil } diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go index 3984cdcc..10d179ff 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entries.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entries.go @@ -82,7 +82,7 @@ func NewGetLedgerEntriesHandler(logger *log.Entry, ledgerEntryReader db.LedgerEn ledgerKeys = append(ledgerKeys, ledgerKey) } - tx, err := ledgerEntryReader.NewTx(ctx) + tx, err := ledgerEntryReader.NewTx(ctx, false) if err != nil { return GetLedgerEntriesResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, diff --git a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go index 6146d48f..7e87217a 100644 --- a/cmd/soroban-rpc/internal/methods/get_ledger_entry.go +++ b/cmd/soroban-rpc/internal/methods/get_ledger_entry.go @@ -63,7 +63,7 @@ func NewGetLedgerEntryHandler(logger *log.Entry, ledgerEntryReader db.LedgerEntr } } - tx, err := ledgerEntryReader.NewTx(ctx) + tx, err := ledgerEntryReader.NewTx(ctx, false) if err != nil { return GetLedgerEntryResponse{}, &jrpc2.Error{ Code: jrpc2.InternalError, diff --git a/cmd/soroban-rpc/internal/methods/simulate_transaction.go b/cmd/soroban-rpc/internal/methods/simulate_transaction.go index 2a26b08a..8d0a5fe4 100644 --- a/cmd/soroban-rpc/internal/methods/simulate_transaction.go +++ b/cmd/soroban-rpc/internal/methods/simulate_transaction.go @@ -270,7 +270,7 @@ func NewSimulateTransactionHandler(logger *log.Entry, ledgerEntryReader db.Ledge } } - readTx, err := ledgerEntryReader.NewCachedTx(ctx) + readTx, err := ledgerEntryReader.NewTx(ctx, true) if err != nil { return SimulateTransactionResponse{ Error: "Cannot create read transaction", diff --git a/cmd/soroban-rpc/internal/preflight/preflight_test.go b/cmd/soroban-rpc/internal/preflight/preflight_test.go index f99d489c..0de5417e 100644 --- a/cmd/soroban-rpc/internal/preflight/preflight_test.go +++ b/cmd/soroban-rpc/internal/preflight/preflight_test.go @@ -350,9 +350,9 @@ func getPreflightParameters(t testing.TB, dbConfig *preflightParametersDBConfig) entryReader := db.NewLedgerEntryReader(dbConfig.dbInstance) var err error if dbConfig.disableCache { - ledgerEntryReadTx, err = entryReader.NewTx(context.Background()) + ledgerEntryReadTx, err = entryReader.NewTx(context.Background(), false) } else { - ledgerEntryReadTx, err = entryReader.NewCachedTx(context.Background()) + ledgerEntryReadTx, err = entryReader.NewTx(context.Background(), true) } require.NoError(t, err) } else {