Merge remote-tracking branch 'origin/v1.10.26-statediff-v4' into telackey/logging_001
# Conflicts: # statediff/service.go
This commit is contained in:
commit
906477771e
@ -287,14 +287,13 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||
}
|
||||
}
|
||||
p := statediff.Config{
|
||||
IndexerConfig: indexerConfig,
|
||||
KnownGapsFilePath: ctx.String(utils.StateDiffKnownGapsFilePath.Name),
|
||||
ID: nodeID,
|
||||
ClientName: clientName,
|
||||
Context: context.Background(),
|
||||
EnableWriteLoop: ctx.Bool(utils.StateDiffWritingFlag.Name),
|
||||
NumWorkers: ctx.Uint(utils.StateDiffWorkersFlag.Name),
|
||||
WaitForSync: ctx.Bool(utils.StateDiffWaitForSync.Name),
|
||||
IndexerConfig: indexerConfig,
|
||||
ID: nodeID,
|
||||
ClientName: clientName,
|
||||
Context: context.Background(),
|
||||
EnableWriteLoop: ctx.Bool(utils.StateDiffWritingFlag.Name),
|
||||
NumWorkers: ctx.Uint(utils.StateDiffWorkersFlag.Name),
|
||||
WaitForSync: ctx.Bool(utils.StateDiffWaitForSync.Name),
|
||||
}
|
||||
utils.RegisterStateDiffService(stack, eth, &cfg.Eth, p, backend)
|
||||
}
|
||||
|
@ -176,7 +176,6 @@ var (
|
||||
utils.StateDiffFileMode,
|
||||
utils.StateDiffFileCsvDir,
|
||||
utils.StateDiffFilePath,
|
||||
utils.StateDiffKnownGapsFilePath,
|
||||
utils.StateDiffWaitForSync,
|
||||
utils.StateDiffWatchedAddressesFilePath,
|
||||
utils.StateDiffUpsert,
|
||||
|
@ -1064,11 +1064,6 @@ var (
|
||||
Name: "statediff.file.path",
|
||||
Usage: "Full path (including filename) to write statediff data out to when operating in sql file mode",
|
||||
}
|
||||
StateDiffKnownGapsFilePath = &cli.StringFlag{
|
||||
Name: "statediff.knowngapsfile.path",
|
||||
Usage: "Full path (including filename) to write knownGaps statements when the DB is unavailable.",
|
||||
Value: "./known_gaps.sql",
|
||||
}
|
||||
StateDiffWatchedAddressesFilePath = &cli.StringFlag{
|
||||
Name: "statediff.file.wapath",
|
||||
Usage: "Full path (including filename) to write statediff watched addresses out to when operating in file mode",
|
||||
|
@ -30,8 +30,6 @@ import (
|
||||
type Config struct {
|
||||
// The configuration used for the stateDiff Indexer
|
||||
IndexerConfig interfaces.Config
|
||||
// The filepath to write knownGaps insert statements if we can't connect to the DB.
|
||||
KnownGapsFilePath string
|
||||
// A unique ID used for this service
|
||||
ID string
|
||||
// Name for the client this service is running
|
||||
|
@ -1,273 +0,0 @@
|
||||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package statediff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
)
|
||||
|
||||
var (
|
||||
knownGapsInsert = "INSERT INTO eth_meta.known_gaps (starting_block_number, ending_block_number, checked_out, processing_key) " +
|
||||
"VALUES ('%s', '%s', %t, %d) " +
|
||||
"ON CONFLICT (starting_block_number) DO UPDATE SET (ending_block_number, processing_key) = ('%s', %d) " +
|
||||
"WHERE eth_meta.known_gaps.ending_block_number <= '%s';\n"
|
||||
dbQueryString = "SELECT MAX(block_number) FROM eth.header_cids"
|
||||
defaultWriteFilePath = "./known_gaps.sql"
|
||||
)
|
||||
|
||||
type KnownGapsState struct {
|
||||
// Should we check for gaps by looking at the DB and comparing the latest block with head
|
||||
checkForGaps bool
|
||||
// Arbitrary processingKey that can be used down the line to differentiate different geth nodes.
|
||||
processingKey int64
|
||||
// This number indicates the expected difference between blocks.
|
||||
// Currently, this is 1 since the geth node processes each block. But down the road this can be used in
|
||||
// Tandom with the processingKey to differentiate block processing logic.
|
||||
expectedDifference *big.Int
|
||||
// Indicates if Geth is in an error state
|
||||
// This is used to indicate the right time to upserts
|
||||
errorState bool
|
||||
// This array keeps track of errorBlocks as they occur.
|
||||
// When the errorState is false again, we can process these blocks.
|
||||
// Do we need a list, can we have /KnownStartErrorBlock and knownEndErrorBlock ints instead?
|
||||
knownErrorBlocks []*big.Int
|
||||
// The filepath to write SQL statements if we can't connect to the DB.
|
||||
writeFilePath string
|
||||
// DB object to use for reading and writing to the DB
|
||||
db sql.Database
|
||||
//Do we have entries in the local sql file that need to be written to the DB
|
||||
sqlFileWaitingForWrite bool
|
||||
// Metrics object used to track metrics.
|
||||
statediffMetrics statediffMetricsHandles
|
||||
}
|
||||
|
||||
// Create a new KnownGapsState struct, currently unused.
|
||||
func NewKnownGapsState(checkForGaps bool, processingKey int64, expectedDifference *big.Int, errorState bool, writeFilePath string, db sql.Database, statediffMetrics statediffMetricsHandles) *KnownGapsState {
|
||||
return &KnownGapsState{
|
||||
checkForGaps: checkForGaps,
|
||||
processingKey: processingKey,
|
||||
expectedDifference: expectedDifference,
|
||||
errorState: errorState,
|
||||
writeFilePath: writeFilePath,
|
||||
db: db,
|
||||
statediffMetrics: statediffMetrics,
|
||||
}
|
||||
}
|
||||
|
||||
func minMax(array []*big.Int) (*big.Int, *big.Int) {
|
||||
var max *big.Int = array[0]
|
||||
var min *big.Int = array[0]
|
||||
for _, value := range array {
|
||||
if max.Cmp(value) == -1 {
|
||||
max = value
|
||||
}
|
||||
if min.Cmp(value) == 1 {
|
||||
min = value
|
||||
}
|
||||
}
|
||||
return min, max
|
||||
}
|
||||
|
||||
// This function actually performs the write of the known gaps. It will try to do the following, it only goes to the next step if a failure occurs.
|
||||
// 1. Write to the DB directly.
|
||||
// 2. Write to sql file locally.
|
||||
// 3. Write to prometheus directly.
|
||||
// 4. Logs and error.
|
||||
func (kg *KnownGapsState) pushKnownGaps(startingBlockNumber *big.Int, endingBlockNumber *big.Int, checkedOut bool, processingKey int64) error {
|
||||
if startingBlockNumber.Cmp(endingBlockNumber) == 1 {
|
||||
return fmt.Errorf("Starting Block %d, is greater than ending block %d", startingBlockNumber, endingBlockNumber)
|
||||
}
|
||||
knownGap := models.KnownGapsModel{
|
||||
StartingBlockNumber: startingBlockNumber.String(),
|
||||
EndingBlockNumber: endingBlockNumber.String(),
|
||||
CheckedOut: checkedOut,
|
||||
ProcessingKey: processingKey,
|
||||
}
|
||||
|
||||
log.Info("Updating Metrics for the start and end block")
|
||||
kg.statediffMetrics.knownGapStart.Update(startingBlockNumber.Int64())
|
||||
kg.statediffMetrics.knownGapEnd.Update(endingBlockNumber.Int64())
|
||||
|
||||
var writeErr error
|
||||
log.Info("Writing known gaps to the DB")
|
||||
if kg.db != nil {
|
||||
dbErr := kg.upsertKnownGaps(knownGap)
|
||||
if dbErr != nil {
|
||||
log.Warn("Error writing knownGaps to DB, writing them to file instead")
|
||||
writeErr = kg.upsertKnownGapsFile(knownGap)
|
||||
}
|
||||
} else {
|
||||
writeErr = kg.upsertKnownGapsFile(knownGap)
|
||||
}
|
||||
if writeErr != nil {
|
||||
log.Error("Unsuccessful when writing to a file", "Error", writeErr)
|
||||
log.Error("Updating Metrics for the start and end error block")
|
||||
log.Error("Unable to write the following Gaps to DB or File", "startBlock", startingBlockNumber, "endBlock", endingBlockNumber)
|
||||
kg.statediffMetrics.knownGapErrorStart.Update(startingBlockNumber.Int64())
|
||||
kg.statediffMetrics.knownGapErrorEnd.Update(endingBlockNumber.Int64())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is a simple wrapper function to write gaps from a knownErrorBlocks array.
|
||||
func (kg *KnownGapsState) captureErrorBlocks(knownErrorBlocks []*big.Int) {
|
||||
startErrorBlock, endErrorBlock := minMax(knownErrorBlocks)
|
||||
|
||||
log.Warn("The following Gaps were found", "knownErrorBlocks", knownErrorBlocks)
|
||||
log.Warn("Updating known Gaps table", "startErrorBlock", startErrorBlock, "endErrorBlock", endErrorBlock, "processingKey", kg.processingKey)
|
||||
kg.pushKnownGaps(startErrorBlock, endErrorBlock, false, kg.processingKey)
|
||||
}
|
||||
|
||||
// Users provide the latestBlockInDb and the latestBlockOnChain
|
||||
// as well as the expected difference. This function does some simple math.
|
||||
// The expected difference for the time being is going to be 1, but as we run
|
||||
// More geth nodes, the expected difference might fluctuate.
|
||||
func isGap(latestBlockInDb *big.Int, latestBlockOnChain *big.Int, expectedDifference *big.Int) bool {
|
||||
latestBlock := big.NewInt(0)
|
||||
if latestBlock.Sub(latestBlockOnChain, expectedDifference).Cmp(latestBlockInDb) != 0 {
|
||||
log.Warn("We found a gap", "latestBlockInDb", latestBlockInDb, "latestBlockOnChain", latestBlockOnChain, "expectedDifference", expectedDifference)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// This function will check for Gaps and update the DB if gaps are found.
|
||||
// The processingKey will currently be set to 0, but as we start to leverage horizontal scaling
|
||||
// It might be a useful parameter to update depending on the geth node.
|
||||
// TODO:
|
||||
// REmove the return value
|
||||
// Write to file if err in writing to DB
|
||||
func (kg *KnownGapsState) findAndUpdateGaps(latestBlockOnChain *big.Int, expectedDifference *big.Int, processingKey int64) error {
|
||||
// Make this global
|
||||
latestBlockInDb, err := kg.queryDbToBigInt(dbQueryString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gapExists := isGap(latestBlockInDb, latestBlockOnChain, expectedDifference)
|
||||
if gapExists {
|
||||
startBlock := big.NewInt(0)
|
||||
endBlock := big.NewInt(0)
|
||||
startBlock.Add(latestBlockInDb, expectedDifference)
|
||||
endBlock.Sub(latestBlockOnChain, expectedDifference)
|
||||
|
||||
log.Warn("Found Gaps starting at", "startBlock", startBlock, "endingBlock", endBlock)
|
||||
err := kg.pushKnownGaps(startBlock, endBlock, false, processingKey)
|
||||
if err != nil {
|
||||
log.Error("We were unable to write the following gap to the DB", "start Block", startBlock, "endBlock", endBlock, "error", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Upserts known gaps to the DB.
|
||||
func (kg *KnownGapsState) upsertKnownGaps(knownGaps models.KnownGapsModel) error {
|
||||
_, err := kg.db.Exec(context.Background(), kg.db.InsertKnownGapsStm(),
|
||||
knownGaps.StartingBlockNumber, knownGaps.EndingBlockNumber, knownGaps.CheckedOut, knownGaps.ProcessingKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error upserting known_gaps entry: %v", err)
|
||||
}
|
||||
log.Info("Successfully Wrote gaps to the DB", "startBlock", knownGaps.StartingBlockNumber, "endBlock", knownGaps.EndingBlockNumber)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write upsert statement into a local file.
|
||||
func (kg *KnownGapsState) upsertKnownGapsFile(knownGaps models.KnownGapsModel) error {
|
||||
insertStmt := []byte(fmt.Sprintf(knownGapsInsert, knownGaps.StartingBlockNumber, knownGaps.EndingBlockNumber, knownGaps.CheckedOut, knownGaps.ProcessingKey,
|
||||
knownGaps.EndingBlockNumber, knownGaps.ProcessingKey, knownGaps.EndingBlockNumber))
|
||||
log.Info("Trying to write file")
|
||||
if kg.writeFilePath == "" {
|
||||
kg.writeFilePath = defaultWriteFilePath
|
||||
}
|
||||
f, err := os.OpenFile(kg.writeFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Info("Unable to open a file for writing")
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err = f.Write(insertStmt); err != nil {
|
||||
log.Info("Unable to open write insert statement to file")
|
||||
return err
|
||||
}
|
||||
log.Info("Wrote the gaps to a local SQL file")
|
||||
kg.sqlFileWaitingForWrite = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kg *KnownGapsState) writeSqlFileStmtToDb() error {
|
||||
log.Info("Writing the local SQL file for KnownGaps to the DB")
|
||||
file, err := ioutil.ReadFile(kg.writeFilePath)
|
||||
|
||||
if err != nil {
|
||||
log.Error("Unable to open local SQL File for writing")
|
||||
return err
|
||||
}
|
||||
|
||||
requests := strings.Split(string(file), ";")
|
||||
|
||||
for _, request := range requests {
|
||||
_, err := kg.db.Exec(context.Background(), request)
|
||||
if err != nil {
|
||||
log.Error("Unable to run insert statement from file to the DB")
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := os.Truncate(kg.writeFilePath, 0); err != nil {
|
||||
log.Info("Failed to empty knownGaps file after inserting statements to the DB", "error", err)
|
||||
}
|
||||
kg.sqlFileWaitingForWrite = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is a simple wrapper function which will run QueryRow on the DB
|
||||
func (kg *KnownGapsState) queryDb(queryString string) (string, error) {
|
||||
var ret string
|
||||
err := kg.db.QueryRow(context.Background(), queryString).Scan(&ret)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprint("Can't properly query the DB for query: ", queryString))
|
||||
return "", err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// This function is a simple wrapper which will call QueryDb but the return value will be
|
||||
// a big int instead of a string
|
||||
func (kg *KnownGapsState) queryDbToBigInt(queryString string) (*big.Int, error) {
|
||||
ret := new(big.Int)
|
||||
res, err := kg.queryDb(queryString)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret, ok := ret.SetString(res, 10)
|
||||
if !ok {
|
||||
log.Error(fmt.Sprint("Can't turn the res ", res, "into a bigInt"))
|
||||
return ret, fmt.Errorf("Can't turn %s into a bigInt", res)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
@ -1,207 +0,0 @@
|
||||
package statediff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
knownGapsFilePath = "./known_gaps.sql"
|
||||
)
|
||||
|
||||
type gapValues struct {
|
||||
knownErrorBlocksStart int64
|
||||
knownErrorBlocksEnd int64
|
||||
expectedDif int64
|
||||
processingKey int64
|
||||
}
|
||||
|
||||
// Add clean db
|
||||
// Test for failures when they are expected, when we go from smaller block to larger block
|
||||
// We should no longer see the smaller block in DB
|
||||
func TestKnownGaps(t *testing.T) {
|
||||
tests := []gapValues{
|
||||
// Known Gaps
|
||||
{knownErrorBlocksStart: 115, knownErrorBlocksEnd: 120, expectedDif: 1, processingKey: 1},
|
||||
/// Same tests as above with a new expected DIF
|
||||
{knownErrorBlocksStart: 1150, knownErrorBlocksEnd: 1200, expectedDif: 2, processingKey: 2},
|
||||
// Test update when block number is larger!!
|
||||
{knownErrorBlocksStart: 1150, knownErrorBlocksEnd: 1204, expectedDif: 2, processingKey: 2},
|
||||
// Update when processing key is different!
|
||||
{knownErrorBlocksStart: 1150, knownErrorBlocksEnd: 1204, expectedDif: 2, processingKey: 10},
|
||||
}
|
||||
|
||||
testWriteToDb(t, tests, true)
|
||||
testWriteToFile(t, tests, true)
|
||||
testFindAndUpdateGaps(t, true)
|
||||
}
|
||||
|
||||
// test writing blocks to the DB
|
||||
func testWriteToDb(t *testing.T, tests []gapValues, wipeDbBeforeStart bool) {
|
||||
t.Log("Starting Write to DB test")
|
||||
db := setupDb(t)
|
||||
|
||||
// Clear Table first, this is needed because we updated an entry to have a larger endblock number
|
||||
// so we can't find the original start and endblock pair.
|
||||
if wipeDbBeforeStart {
|
||||
t.Log("Cleaning up eth_meta.known_gaps table")
|
||||
db.Exec(context.Background(), "DELETE FROM eth_meta.known_gaps")
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
// Create an array with knownGaps based on user inputs
|
||||
knownGaps := KnownGapsState{
|
||||
processingKey: tc.processingKey,
|
||||
expectedDifference: big.NewInt(tc.expectedDif),
|
||||
db: db,
|
||||
statediffMetrics: RegisterStatediffMetrics(metrics.DefaultRegistry),
|
||||
}
|
||||
service := &Service{
|
||||
KnownGaps: knownGaps,
|
||||
}
|
||||
knownErrorBlocks := (make([]*big.Int, 0))
|
||||
knownErrorBlocks = createKnownErrorBlocks(knownErrorBlocks, tc.knownErrorBlocksStart, tc.knownErrorBlocksEnd)
|
||||
service.KnownGaps.knownErrorBlocks = knownErrorBlocks
|
||||
// Upsert
|
||||
testCaptureErrorBlocks(t, service)
|
||||
// Validate that the upsert was done correctly.
|
||||
validateUpsert(t, service, tc.knownErrorBlocksStart, tc.knownErrorBlocksEnd)
|
||||
}
|
||||
tearDown(t, db)
|
||||
}
|
||||
|
||||
// test writing blocks to file and then inserting them to DB
|
||||
func testWriteToFile(t *testing.T, tests []gapValues, wipeDbBeforeStart bool) {
|
||||
t.Log("Starting write to file test")
|
||||
db := setupDb(t)
|
||||
// Clear Table first, this is needed because we updated an entry to have a larger endblock number
|
||||
// so we can't find the original start and endblock pair.
|
||||
if wipeDbBeforeStart {
|
||||
t.Log("Cleaning up eth_meta.known_gaps table")
|
||||
db.Exec(context.Background(), "DELETE FROM eth_meta.known_gaps")
|
||||
}
|
||||
if _, err := os.Stat(knownGapsFilePath); err == nil {
|
||||
err := os.Remove(knownGapsFilePath)
|
||||
if err != nil {
|
||||
t.Fatal("Can't delete local file")
|
||||
}
|
||||
}
|
||||
tearDown(t, db)
|
||||
for _, tc := range tests {
|
||||
knownGaps := KnownGapsState{
|
||||
processingKey: tc.processingKey,
|
||||
expectedDifference: big.NewInt(tc.expectedDif),
|
||||
writeFilePath: knownGapsFilePath,
|
||||
statediffMetrics: RegisterStatediffMetrics(metrics.DefaultRegistry),
|
||||
db: nil, // Only set to nil to be verbose that we can't use it
|
||||
}
|
||||
service := &Service{
|
||||
KnownGaps: knownGaps,
|
||||
}
|
||||
knownErrorBlocks := (make([]*big.Int, 0))
|
||||
knownErrorBlocks = createKnownErrorBlocks(knownErrorBlocks, tc.knownErrorBlocksStart, tc.knownErrorBlocksEnd)
|
||||
service.KnownGaps.knownErrorBlocks = knownErrorBlocks
|
||||
|
||||
testCaptureErrorBlocks(t, service)
|
||||
newDb := setupDb(t)
|
||||
service.KnownGaps.db = newDb
|
||||
if service.KnownGaps.sqlFileWaitingForWrite {
|
||||
writeErr := service.KnownGaps.writeSqlFileStmtToDb()
|
||||
require.NoError(t, writeErr)
|
||||
}
|
||||
|
||||
// Validate that the upsert was done correctly.
|
||||
validateUpsert(t, service, tc.knownErrorBlocksStart, tc.knownErrorBlocksEnd)
|
||||
tearDown(t, newDb)
|
||||
}
|
||||
}
|
||||
|
||||
// Find a gap, if no gaps exist, it will create an arbitrary one
|
||||
func testFindAndUpdateGaps(t *testing.T, wipeDbBeforeStart bool) {
|
||||
db := setupDb(t)
|
||||
|
||||
if wipeDbBeforeStart {
|
||||
db.Exec(context.Background(), "DELETE FROM eth_meta.known_gaps")
|
||||
}
|
||||
knownGaps := KnownGapsState{
|
||||
processingKey: 1,
|
||||
expectedDifference: big.NewInt(1),
|
||||
db: db,
|
||||
statediffMetrics: RegisterStatediffMetrics(metrics.DefaultRegistry),
|
||||
}
|
||||
service := &Service{
|
||||
KnownGaps: knownGaps,
|
||||
}
|
||||
|
||||
latestBlockInDb, err := service.KnownGaps.queryDbToBigInt("SELECT MAX(block_number) FROM eth.header_cids")
|
||||
if err != nil {
|
||||
t.Skip("Can't find a block in the eth.header_cids table.. Please put one there")
|
||||
}
|
||||
|
||||
// Add the gapDifference for testing purposes
|
||||
gapDifference := big.NewInt(10) // Set a difference between latestBlock in DB and on Chain
|
||||
expectedDifference := big.NewInt(1) // Set what the expected difference between latestBlock in DB and on Chain should be
|
||||
|
||||
latestBlockOnChain := big.NewInt(0)
|
||||
latestBlockOnChain.Add(latestBlockInDb, gapDifference)
|
||||
|
||||
t.Log("The latest block on the chain is: ", latestBlockOnChain)
|
||||
t.Log("The latest block on the DB is: ", latestBlockInDb)
|
||||
|
||||
gapUpsertErr := service.KnownGaps.findAndUpdateGaps(latestBlockOnChain, expectedDifference, 0)
|
||||
require.NoError(t, gapUpsertErr)
|
||||
|
||||
startBlock := big.NewInt(0)
|
||||
endBlock := big.NewInt(0)
|
||||
|
||||
startBlock.Add(latestBlockInDb, expectedDifference)
|
||||
endBlock.Sub(latestBlockOnChain, expectedDifference)
|
||||
validateUpsert(t, service, startBlock.Int64(), endBlock.Int64())
|
||||
}
|
||||
|
||||
// test capturing missed blocks
|
||||
func testCaptureErrorBlocks(t *testing.T, service *Service) {
|
||||
service.KnownGaps.captureErrorBlocks(service.KnownGaps.knownErrorBlocks)
|
||||
}
|
||||
|
||||
// Helper function to create an array of gaps given a start and end block
|
||||
func createKnownErrorBlocks(knownErrorBlocks []*big.Int, knownErrorBlocksStart int64, knownErrorBlocksEnd int64) []*big.Int {
|
||||
for i := knownErrorBlocksStart; i <= knownErrorBlocksEnd; i++ {
|
||||
knownErrorBlocks = append(knownErrorBlocks, big.NewInt(i))
|
||||
}
|
||||
return knownErrorBlocks
|
||||
}
|
||||
|
||||
// Make sure the upsert was performed correctly
|
||||
func validateUpsert(t *testing.T, service *Service, startingBlock int64, endingBlock int64) {
|
||||
t.Logf("Starting to query blocks: %d - %d", startingBlock, endingBlock)
|
||||
queryString := fmt.Sprintf("SELECT starting_block_number from eth_meta.known_gaps WHERE starting_block_number = %d AND ending_block_number = %d", startingBlock, endingBlock)
|
||||
|
||||
_, queryErr := service.KnownGaps.queryDb(queryString) // Figure out the string.
|
||||
t.Logf("Updated Known Gaps table starting from, %d, and ending at, %d", startingBlock, endingBlock)
|
||||
require.NoError(t, queryErr)
|
||||
}
|
||||
|
||||
// Create a DB object to use
|
||||
func setupDb(t *testing.T) sql.Database {
|
||||
db, err := postgres.SetupSQLXDB()
|
||||
if err != nil {
|
||||
t.Error("Can't create a DB connection....")
|
||||
t.Fatal(err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
// Teardown the DB
|
||||
func tearDown(t *testing.T, db sql.Database) {
|
||||
t.Log("Starting tearDown")
|
||||
db.Close()
|
||||
}
|
@ -41,10 +41,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
ind "github.com/ethereum/go-ethereum/statediff/indexer"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/interfaces"
|
||||
nodeinfo "github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/shared"
|
||||
types2 "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/thoas/go-funk"
|
||||
@ -131,9 +129,7 @@ type Service struct {
|
||||
BackendAPI ethapi.Backend
|
||||
// Should the statediff service wait for geth to sync to head?
|
||||
WaitForSync bool
|
||||
// Used to signal if we should check for KnownGaps
|
||||
KnownGaps KnownGapsState
|
||||
// Whether or not we have any subscribers; only if we do, do we processes state diffs
|
||||
// Whether we have any subscribers
|
||||
subscribers int32
|
||||
// Interface for publishing statediffs as PG-IPLD objects
|
||||
indexer interfaces.StateDiffIndexer
|
||||
@ -164,7 +160,6 @@ func NewBlockCache(max uint) BlockCache {
|
||||
func New(stack *node.Node, ethServ *eth.Ethereum, cfg *ethconfig.Config, params Config, backend ethapi.Backend) error {
|
||||
blockChain := ethServ.BlockChain()
|
||||
var indexer interfaces.StateDiffIndexer
|
||||
var db sql.Database
|
||||
var err error
|
||||
quitCh := make(chan bool)
|
||||
indexerConfigAvailable := params.IndexerConfig != nil
|
||||
@ -177,7 +172,7 @@ func New(stack *node.Node, ethServ *eth.Ethereum, cfg *ethconfig.Config, params
|
||||
ClientName: params.ClientName,
|
||||
}
|
||||
var err error
|
||||
db, indexer, err = ind.NewStateDiffIndexer(params.Context, blockChain.Config(), info, params.IndexerConfig)
|
||||
_, indexer, err = ind.NewStateDiffIndexer(params.Context, blockChain.Config(), info, params.IndexerConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -188,25 +183,7 @@ func New(stack *node.Node, ethServ *eth.Ethereum, cfg *ethconfig.Config, params
|
||||
if workers == 0 {
|
||||
workers = 1
|
||||
}
|
||||
// If we ever have multiple processingKeys we can update them here
|
||||
// along with the expectedDifference
|
||||
knownGaps := &KnownGapsState{
|
||||
processingKey: 0,
|
||||
expectedDifference: big.NewInt(1),
|
||||
errorState: false,
|
||||
writeFilePath: params.KnownGapsFilePath,
|
||||
db: db,
|
||||
statediffMetrics: defaultStatediffMetrics,
|
||||
sqlFileWaitingForWrite: false,
|
||||
}
|
||||
if indexerConfigAvailable {
|
||||
if params.IndexerConfig.Type() == shared.POSTGRES {
|
||||
knownGaps.checkForGaps = true
|
||||
} else {
|
||||
log.Info("We are not going to check for gaps on start up since we are not connected to Postgres!")
|
||||
knownGaps.checkForGaps = false
|
||||
}
|
||||
}
|
||||
|
||||
sds := &Service{
|
||||
Mutex: sync.Mutex{},
|
||||
BlockChain: blockChain,
|
||||
@ -217,7 +194,6 @@ func New(stack *node.Node, ethServ *eth.Ethereum, cfg *ethconfig.Config, params
|
||||
BlockCache: NewBlockCache(workers),
|
||||
BackendAPI: backend,
|
||||
WaitForSync: params.WaitForSync,
|
||||
KnownGaps: *knownGaps,
|
||||
indexer: indexer,
|
||||
enableWriteLoop: params.EnableWriteLoop,
|
||||
numWorkers: workers,
|
||||
@ -352,43 +328,15 @@ func (sds *Service) writeLoopWorker(params workerParams) {
|
||||
sds.writeGenesisStateDiff(parentBlock, params.id)
|
||||
}
|
||||
|
||||
// If for any reason we need to check for gaps,
|
||||
// Check and update the gaps table.
|
||||
if sds.KnownGaps.checkForGaps && !sds.KnownGaps.errorState {
|
||||
log.Info("Checking for Gaps at", "current block", currentBlock.Number())
|
||||
go sds.KnownGaps.findAndUpdateGaps(currentBlock.Number(), sds.KnownGaps.expectedDifference, sds.KnownGaps.processingKey)
|
||||
sds.KnownGaps.checkForGaps = false
|
||||
}
|
||||
|
||||
log.Info("Writing state diff", "block height", currentBlock.Number().Uint64(), "worker", params.id)
|
||||
writeLoopParams.RLock()
|
||||
err := sds.writeStateDiffWithRetry(currentBlock, parentBlock.Root(), writeLoopParams.Params)
|
||||
writeLoopParams.RUnlock()
|
||||
if err != nil {
|
||||
// This is where the Postgres errors bubbles up to, so this is where we want to emit a comprehensie error trace/report
|
||||
log.Error("statediff.Service.WriteLoop: processing error", "block height", currentBlock.Number().Uint64(), "error", err.Error(), "worker", params.id)
|
||||
sds.KnownGaps.errorState = true
|
||||
log.Warn("Updating the following block to knownErrorBlocks to be inserted into knownGaps table", "blockNumber", currentBlock.Number())
|
||||
sds.KnownGaps.knownErrorBlocks = append(sds.KnownGaps.knownErrorBlocks, currentBlock.Number())
|
||||
// Write object to startdiff
|
||||
continue
|
||||
}
|
||||
sds.KnownGaps.errorState = false
|
||||
if sds.KnownGaps.knownErrorBlocks != nil {
|
||||
// We must pass in parameters by VALUE not reference.
|
||||
// If we pass them in my reference, the references can change before the computation is complete!
|
||||
staticKnownErrorBlocks := make([]*big.Int, len(sds.KnownGaps.knownErrorBlocks))
|
||||
copy(staticKnownErrorBlocks, sds.KnownGaps.knownErrorBlocks)
|
||||
sds.KnownGaps.knownErrorBlocks = nil
|
||||
go sds.KnownGaps.captureErrorBlocks(staticKnownErrorBlocks)
|
||||
}
|
||||
|
||||
if sds.KnownGaps.sqlFileWaitingForWrite {
|
||||
log.Info("There are entries in the SQL file for knownGaps that should be written")
|
||||
err := sds.KnownGaps.writeSqlFileStmtToDb()
|
||||
if err != nil {
|
||||
log.Error("Unable to write KnownGap sql file to DB")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: how to handle with concurrent workers
|
||||
defaultStatediffMetrics.lastStatediffHeight.Update(int64(currentBlock.Number().Uint64()))
|
||||
@ -641,7 +589,7 @@ func (sds *Service) Unsubscribe(id rpc.ID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This function will check the status of geth syncing.
|
||||
// GetSyncStatus will check the status of geth syncing.
|
||||
// It will return false if geth has finished syncing.
|
||||
// It will return a true Geth is still syncing.
|
||||
func (sds *Service) GetSyncStatus(pubEthAPI *ethapi.EthereumAPI) (bool, error) {
|
||||
@ -656,7 +604,7 @@ func (sds *Service) GetSyncStatus(pubEthAPI *ethapi.EthereumAPI) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// This function calls GetSyncStatus to check if we have caught up to head.
|
||||
// WaitingForSync calls GetSyncStatus to check if we have caught up to head.
|
||||
// It will keep looking and checking if we have caught up to head.
|
||||
// It will only complete if we catch up to head, otherwise it will keep looping forever.
|
||||
func (sds *Service) WaitingForSync() error {
|
||||
@ -900,7 +848,7 @@ func (sds *Service) writeStateDiffWithRetry(block *types.Block, parentRoot commo
|
||||
return err
|
||||
}
|
||||
|
||||
// Performs one of following operations on the watched addresses in writeLoopParams and the db:
|
||||
// WatchAddress performs one of following operations on the watched addresses in writeLoopParams and the db:
|
||||
// add | remove | set | clear
|
||||
func (sds *Service) WatchAddress(operation types2.OperationType, args []types2.WatchAddressArg) error {
|
||||
// lock writeLoopParams for a write
|
||||
|
Loading…
Reference in New Issue
Block a user