github.com/klaytn/klaytn@v1.12.1/datasync/dbsyncer/dbsync_context.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The klaytn library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package dbsyncer
    18  
    19  import (
    20  	"context"
    21  	"database/sql"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/klaytn/klaytn/blockchain/types"
    26  )
    27  
    28  // HandleChainEventContext supports 2PC Commit (insert block + insert txs) for data consistency
    29  // @TODO-Klaytn improve performance, too slower than HanleChainEvent()
    30  func (ds *DBSyncer) HandleChainEventContext(block *types.Block) error {
    31  	logger.Info("dbsyncer HandleChainEvent", "number", block.Number(), "txs", block.Transactions().Len())
    32  	startblock := time.Now()
    33  
    34  	ctx, cancel := context.WithTimeout(ds.ctx, 90*time.Second)
    35  	defer cancel()
    36  
    37  	tx, err := ds.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelDefault})
    38  	if err != nil {
    39  		logger.Error("fail to begin tx", "err", err)
    40  		return err
    41  	}
    42  
    43  	if err := ds.syncBlockHeaderContext(ctx, tx, block); err != nil {
    44  		logger.Error("fail to sync block", "block", block.Number(), "err", err)
    45  		if rerr := tx.Rollback(); rerr != nil {
    46  			logger.Error("fail to rollback tx", "block", block.Number(), "err", rerr)
    47  		}
    48  		return err
    49  	}
    50  
    51  	blocktime := time.Since(startblock)
    52  	starttx := time.Now()
    53  
    54  	if block.Transactions().Len() > 0 {
    55  		if err := ds.syncTransactionsContext(ctx, tx, block); err != nil {
    56  			logger.Error("fail to sync transaction", "block", block.Number(), "err", err)
    57  			if rerr := tx.Rollback(); rerr != nil {
    58  				logger.Error("fail to rollback tx", "block", block.Number(), "err", rerr)
    59  			}
    60  			return err
    61  		}
    62  	}
    63  
    64  	if err := tx.Commit(); err != nil {
    65  		logger.Error("fail to commit tx", "block", block.Number(), "err", err)
    66  		return err
    67  	}
    68  
    69  	txtime := time.Since(starttx)
    70  	totalTime := time.Since(startblock)
    71  	if ds.logMode {
    72  		logger.Info("dbsync time", "number", block.Number(), "block", blocktime, "txs", txtime, "total", totalTime)
    73  	}
    74  
    75  	return nil
    76  }
    77  
    78  func (ds *DBSyncer) syncBlockHeaderContext(ctx context.Context, tx *sql.Tx, block *types.Block) error {
    79  	proposerAddr, committeeAddrs, err := getProposerAndValidatorsFromBlock(block)
    80  
    81  	totalTx := block.Transactions().Len()
    82  	committee := strings.ToLower(committeeAddrs)
    83  	gasUsed := block.Header().GasUsed
    84  	gasPrice := ds.blockchain.Config().UnitPrice
    85  	hash := block.Header().Hash().Hex()
    86  	number := block.Header().Number.Uint64()
    87  	parentHash := block.Header().ParentHash.Hex()
    88  	proposer := strings.ToLower(proposerAddr)
    89  	reward := strings.ToLower(block.Header().Rewardbase.Hex())
    90  	size := block.Size()
    91  	timestamp := block.Header().Time.String()
    92  	timestampFos := block.Header().TimeFoS
    93  
    94  	stmtIns, err := ds.db.Prepare(ds.blockInsertQuery)
    95  	if err != nil {
    96  		logger.Error("fail to prepare (block)", "query", ds.blockInsertQuery)
    97  		return err
    98  	}
    99  	tStmtIns := tx.StmtContext(ctx, stmtIns)
   100  
   101  	defer func() {
   102  		if err := tStmtIns.Close(); err != nil {
   103  			logger.Error("fail to close stmt", "err", err)
   104  		}
   105  	}()
   106  
   107  	if _, err := tStmtIns.Exec(totalTx, committee, gasUsed, gasPrice, hash,
   108  		number, parentHash, proposer, reward, size, timestamp, timestampFos); err != nil {
   109  		logger.Error("fail to insert DB (block)", "number", block.Number(), "err", err)
   110  		return err
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func (ds *DBSyncer) syncTransactionsContext(ctx context.Context, syncTx *sql.Tx, block *types.Block) error {
   117  	txKey := block.NumberU64() * TX_KEY_FACTOR
   118  	txStr, vals, insertCount := ds.resetTxParameter()
   119  	summaryStr, summaryVals, summaryInsertCount := ds.resetSummaryParameter()
   120  	txMapStr, txMapVals, txMapInsertCount := ds.resetTxMapParameter()
   121  
   122  	receipts := ds.blockchain.GetReceiptsByBlockHash(block.Hash())
   123  
   124  	for index, tx := range block.Transactions() {
   125  		txKey += uint64(index)
   126  		cols, val, txMapArg, summaryArg, err := MakeTxDBRow(block, txKey, tx, receipts[index])
   127  		if err != nil {
   128  			return err
   129  		}
   130  
   131  		txStr += cols + ","
   132  		vals = append(vals, val...)
   133  		insertCount++
   134  
   135  		if insertCount >= ds.bulkInsertSize {
   136  			if err := ds.bulkInsertContext(ctx, syncTx, txStr, vals, block, insertCount); err != nil {
   137  				return err
   138  			}
   139  			txStr, vals, insertCount = ds.resetTxParameter()
   140  		}
   141  
   142  		scols, sval, count, err := MakeSummaryDBRow(summaryArg)
   143  
   144  		if count == 1 {
   145  			summaryStr += scols + ","
   146  			summaryVals = append(summaryVals, sval...)
   147  			summaryInsertCount++
   148  		}
   149  
   150  		if summaryInsertCount >= ds.bulkInsertSize {
   151  			if err := ds.bulkInsertContext(ctx, syncTx, summaryStr, summaryVals, block, summaryInsertCount); err != nil {
   152  				return err
   153  			}
   154  			summaryStr, summaryVals, summaryInsertCount = ds.resetTxParameter()
   155  		}
   156  
   157  		tcols, tval, tcount, err := MakeTxMappingRow(txMapArg)
   158  		if err != nil {
   159  			return err
   160  		}
   161  
   162  		if tcount == 1 {
   163  			txMapStr += tcols + ","
   164  			txMapVals = append(txMapVals, tval...)
   165  			txMapInsertCount++
   166  		}
   167  
   168  		if txMapInsertCount >= ds.bulkInsertSize {
   169  			if err := ds.bulkInsertContext(ctx, syncTx, txMapStr, txMapVals, block, txMapInsertCount); err != nil {
   170  				return err
   171  			}
   172  			txMapStr, txMapVals, txMapInsertCount = ds.resetTxMapParameter()
   173  		}
   174  	}
   175  
   176  	if insertCount > 0 {
   177  		if err := ds.bulkInsertContext(ctx, syncTx, txStr, vals, block, insertCount); err != nil {
   178  			return err
   179  		}
   180  	}
   181  
   182  	if summaryInsertCount > 0 {
   183  		if err := ds.bulkInsertContext(ctx, syncTx, summaryStr, summaryVals, block, summaryInsertCount); err != nil {
   184  			return err
   185  		}
   186  	}
   187  
   188  	if txMapInsertCount > 0 {
   189  		if err := ds.bulkInsertContext(ctx, syncTx, txMapStr, txMapVals, block, txMapInsertCount); err != nil {
   190  			return err
   191  		}
   192  	}
   193  
   194  	return nil
   195  }
   196  
   197  func (ds *DBSyncer) bulkInsertContext(ctx context.Context, tx *sql.Tx, sqlStr string, vals []interface{}, block *types.Block, insertCount int) error {
   198  	start := time.Now()
   199  	// trim the last
   200  	sqlStr = sqlStr[0 : len(sqlStr)-1]
   201  
   202  	stmtTxs, err := ds.db.Prepare(sqlStr)
   203  	if err != nil {
   204  		logger.Error("fail to create prepare", "sql", sqlStr, "err", err)
   205  		return err
   206  	}
   207  	tStmtTxs := tx.StmtContext(ctx, stmtTxs)
   208  
   209  	if _, err := tStmtTxs.Exec(vals...); err != nil {
   210  		logger.Error("fail to insert DB (tx)", "number", block.Number(), "err", err)
   211  		return err
   212  	}
   213  	defer func() {
   214  		if err := tStmtTxs.Close(); err != nil {
   215  			logger.Error("fail to close stmt", "err", err)
   216  		}
   217  	}()
   218  
   219  	if ds.logMode {
   220  		txTime := time.Since(start)
   221  		logger.Info("TX INSERT", "counts", insertCount, "time", txTime)
   222  	}
   223  
   224  	return nil
   225  }