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 }