Skip to content

Commit

Permalink
FOR REVIEW ONLY: indexer 2.15.1 into develop (#1401)
Browse files Browse the repository at this point in the history
Co-authored-by: Will Winder <[email protected]>
Co-authored-by: Arthur Kepler <[email protected]>
Co-authored-by: Eric Warehime <[email protected]>
  • Loading branch information
4 people authored Jan 13, 2023
1 parent dd496ed commit ebacd84
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.15.0
2.15.1
5 changes: 0 additions & 5 deletions api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1361,11 +1361,6 @@ func (si *ServerImplementation) fetchBlock(ctx context.Context, round uint64, op

results := make([]generated.Transaction, 0)
for _, txrow := range transactions {
// Do not include inner transactions.
if txrow.RootTxn != nil {
continue
}

tx, err := txnRowToTransaction(txrow)
if err != nil {
return err
Expand Down
7 changes: 7 additions & 0 deletions idb/idb.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ type GetBlockOptions struct {

// TransactionFilter is a parameter object with all the transaction filter options.
type TransactionFilter struct {
// SkipOptimization is used for testing to ensure the parameters are not modified.
SkipOptimization bool

// Address filtering transactions for one Address will
// return transactions newest-first proceding into the
// past. Paging through such results can be achieved by
Expand Down Expand Up @@ -241,6 +244,10 @@ type TransactionFilter struct {
// instead of the root txn.
ReturnInnerTxnOnly bool

// If this flag is set to true, then the query returns only root txns
// and no inner txns
ReturnRootTxnsOnly bool

// If this flag is set to true, then the query returns the block excluding
// the transactions
HeaderOnly bool
Expand Down
34 changes: 31 additions & 3 deletions idb/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"os"
"reflect"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -466,7 +467,7 @@ func (db *IndexerDb) GetBlock(ctx context.Context, round uint64, options idb.Get

if options.Transactions {
out := make(chan idb.TxnRow, 1)
query, whereArgs, err := buildTransactionQuery(idb.TransactionFilter{Round: &round, Limit: options.MaxTransactionsLimit + 1})
query, whereArgs, err := buildTransactionQuery(idb.TransactionFilter{Round: &round, Limit: options.MaxTransactionsLimit + 1, ReturnRootTxnsOnly: true})
if err != nil {
err = fmt.Errorf("txn query err %v", err)
out <- idb.TxnRow{Error: err}
Expand Down Expand Up @@ -667,9 +668,12 @@ func buildTransactionQuery(tf idb.TransactionFilter) (query string, whereArgs []
if tf.RekeyTo != nil && (*tf.RekeyTo) {
whereParts = append(whereParts, "(t.txn -> 'txn' -> 'rekey') IS NOT NULL")
}
if tf.ReturnRootTxnsOnly {
whereParts = append(whereParts, "t.txid IS NOT NULL")
}

// If returnInnerTxnOnly flag is false, then return the root transaction
if !tf.ReturnInnerTxnOnly {
if !(tf.ReturnInnerTxnOnly || tf.ReturnRootTxnsOnly) {
query = "SELECT t.round, t.intra, t.txn, root.txn, t.extra, t.asset, h.realtime FROM txn t JOIN block_header h ON t.round = h.round"
} else {
query = "SELECT t.round, t.intra, t.txn, NULL, t.extra, t.asset, h.realtime FROM txn t JOIN block_header h ON t.round = h.round"
Expand All @@ -680,7 +684,7 @@ func buildTransactionQuery(tf idb.TransactionFilter) (query string, whereArgs []
}

// join in the root transaction if the returnInnerTxnOnly flag is false
if !tf.ReturnInnerTxnOnly {
if !(tf.ReturnInnerTxnOnly || tf.ReturnRootTxnsOnly) {
query += " LEFT OUTER JOIN txn root ON t.round = root.round AND (t.extra->>'root-intra')::int = root.intra"
}

Expand Down Expand Up @@ -724,9 +728,33 @@ func (db *IndexerDb) yieldTxns(ctx context.Context, tx pgx.Tx, tf idb.Transactio
db.yieldTxnsThreadSimple(rows, out, nil, nil)
}

// txnFilterOptimization checks that there are no parameters set which would
// cause non-contiguous transaction results. As long as all transactions in a
// range are returned, we are guaranteed to fetch the root transactions, and
// therefore do not need to fetch inner transactions.
func txnFilterOptimization(tf idb.TransactionFilter) idb.TransactionFilter {
defaults := idb.TransactionFilter{
Round: tf.Round,
MinRound: tf.MinRound,
MaxRound: tf.MaxRound,
BeforeTime: tf.BeforeTime,
AfterTime: tf.AfterTime,
Limit: tf.Limit,
NextToken: tf.NextToken,
Offset: tf.Offset,
OffsetLT: tf.OffsetLT,
OffsetGT: tf.OffsetGT,
}
if reflect.DeepEqual(tf, defaults) {
tf.ReturnRootTxnsOnly = true
}
return tf
}

// Transactions is part of idb.IndexerDB
func (db *IndexerDb) Transactions(ctx context.Context, tf idb.TransactionFilter) (<-chan idb.TxnRow, uint64) {
out := make(chan idb.TxnRow, 1)
tf = txnFilterOptimization(tf)

tx, err := db.db.BeginTx(ctx, readonlyRepeatableRead)
if err != nil {
Expand Down
14 changes: 8 additions & 6 deletions idb/postgres/postgres_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,7 @@ func TestNonDisplayableUTF8(t *testing.T) {

// Test 3: transaction results properly serialized
// Transaction results also return the inner txn acfg
txnRows, _ := db.Transactions(context.Background(), idb.TransactionFilter{})
txnRows, _ := db.Transactions(context.Background(), idb.TransactionFilter{SkipOptimization: true})
num = 0
for row := range txnRows {
require.NoError(t, row.Error)
Expand Down Expand Up @@ -1316,7 +1316,7 @@ func TestKeytypeResetsOnRekey(t *testing.T) {
err = proc(&rpcs.EncodedBlockCert{Block: block})
require.NoError(t, err)

keytype = "msig"
keytype = generated.AccountSigTypeMsig
assertKeytype(t, db, test.AccountA, &keytype)
}

Expand Down Expand Up @@ -1964,7 +1964,8 @@ func TestBadTxnJsonEncoding(t *testing.T) {
{
offset := uint64(rootIntra)
tf := idb.TransactionFilter{
Offset: &offset,
SkipOptimization: true,
Offset: &offset,
}
rowsCh, _ := db.Transactions(context.Background(), tf)

Expand All @@ -1978,7 +1979,8 @@ func TestBadTxnJsonEncoding(t *testing.T) {
{
offset := uint64(rootIntra) + 1
tf := idb.TransactionFilter{
Offset: &offset,
SkipOptimization: true,
Offset: &offset,
}
rowsCh, _ := db.Transactions(context.Background(), tf)

Expand Down Expand Up @@ -2096,7 +2098,7 @@ func TestTransactionsTxnAhead(t *testing.T) {
require.NoError(t, err)
}
{
rowsCh, _ := db.Transactions(context.Background(), idb.TransactionFilter{})
rowsCh, _ := db.Transactions(context.Background(), idb.TransactionFilter{SkipOptimization: true})
_, ok := <-rowsCh
assert.False(t, ok)
}
Expand All @@ -2110,7 +2112,7 @@ func TestTransactionsTxnAhead(t *testing.T) {
require.NoError(t, err)
}
{
rowsCh, _ := db.Transactions(context.Background(), idb.TransactionFilter{})
rowsCh, _ := db.Transactions(context.Background(), idb.TransactionFilter{SkipOptimization: true})
row, ok := <-rowsCh
require.True(t, ok)
require.NoError(t, row.Error)
Expand Down
56 changes: 56 additions & 0 deletions idb/postgres/postgres_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package postgres

import (
"fmt"
"testing"
"time"

"github.com/stretchr/testify/assert"

"github.com/algorand/indexer/idb"
)

func Test_txnFilterOptimization(t *testing.T) {
tests := []struct {
name string
arg idb.TransactionFilter
rootOnly bool
}{
{
name: "basic",
arg: idb.TransactionFilter{},
rootOnly: true,
},
{
name: "rounds",
arg: idb.TransactionFilter{MinRound: 100, MaxRound: 101, Limit: 100},
rootOnly: true,
},
{
name: "date",
arg: idb.TransactionFilter{AfterTime: time.Unix(100000, 100), Limit: 100},
rootOnly: true,
},
{
name: "token",
arg: idb.TransactionFilter{NextToken: "test", Limit: 100},
rootOnly: true,
},
{
name: "address",
arg: idb.TransactionFilter{Address: []byte{0x10, 0x11, 0x12}, Limit: 100},
rootOnly: false,
},
{
name: "type",
arg: idb.TransactionFilter{TypeEnum: idb.TypeEnumPay, Limit: 100},
rootOnly: false,
},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("%s(%t)", tt.name, tt.rootOnly), func(t *testing.T) {
optimized := txnFilterOptimization(tt.arg)
assert.Equal(t, tt.rootOnly, optimized.ReturnRootTxnsOnly)
})
}
}

0 comments on commit ebacd84

Please sign in to comment.