go.temporal.io/server@v1.23.0/common/persistence/history_node_util.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package persistence 26 27 import ( 28 "fmt" 29 "sort" 30 31 "go.temporal.io/api/serviceerror" 32 33 persistencespb "go.temporal.io/server/api/persistence/v1" 34 "go.temporal.io/server/common" 35 ) 36 37 type ( 38 historyNodeMetadata struct { 39 branchInfo *persistencespb.HistoryBranch 40 41 nodeID int64 42 transactionID int64 43 prevTransactionID int64 44 } 45 ) 46 47 func validateNodeChainAndTrim( 48 tailNodeID int64, 49 tailTransactionID int64, 50 transactionIDToNode map[int64]historyNodeMetadata, 51 ) ([]historyNodeMetadata, error) { 52 53 nodeIDToNodes := indexNodeIDToNode(transactionIDToNode) 54 55 nodeChain, err := reverselyLinkNode( 56 tailNodeID, 57 tailTransactionID, 58 transactionIDToNode, 59 ) 60 if err != nil { 61 return nil, err 62 } 63 64 nodesToTrim := trimNodes(nodeIDToNodes, nodeChain) 65 return nodesToTrim, nil 66 } 67 68 func indexNodeIDToNode( 69 transactionIDToNode map[int64]historyNodeMetadata, 70 ) map[int64][]historyNodeMetadata { 71 // indexing node ID -> sorted nodeMetadata by transaction ID from high to low 72 nodeIDToNodes := make(map[int64][]historyNodeMetadata) 73 for _, node := range transactionIDToNode { 74 nodeMetadata := nodeIDToNodes[node.nodeID] 75 nodeMetadata = append(nodeMetadata, node) 76 nodeIDToNodes[node.nodeID] = nodeMetadata 77 } 78 for nodeID := range nodeIDToNodes { 79 nodes := nodeIDToNodes[nodeID] 80 // reverse sort by transaction ID 81 sort.Slice(nodes, func(i, j int) bool { 82 return nodes[i].transactionID > nodes[j].transactionID 83 }) 84 nodeIDToNodes[nodeID] = nodes 85 } 86 return nodeIDToNodes 87 } 88 89 func reverselyLinkNode( 90 tailNodeID int64, 91 tailTransactionID int64, 92 transactionIDToNode map[int64]historyNodeMetadata, 93 ) ([]historyNodeMetadata, error) { 94 // from tail node, trace back 95 transactionID := tailTransactionID 96 node, ok := transactionIDToNode[transactionID] 97 // sanity check node ID <-> transaction ID being unique 98 if !ok || node.nodeID != tailNodeID { 99 return nil, serviceerror.NewInternal( 100 fmt.Sprintf("unable to find or verify the tail history node, node ID: %v, transaction ID: %v", 101 tailNodeID, 102 tailTransactionID, 103 ), 104 ) 105 } 106 107 var nodes []historyNodeMetadata 108 nodes = append(nodes, node) 109 for node.nodeID > common.FirstEventID { 110 if prevNode, ok := transactionIDToNode[node.prevTransactionID]; !ok { 111 return nil, serviceerror.NewInternal( 112 fmt.Sprintf("unable to back trace history node, node ID: %v, transaction ID: %v, prev transaction ID: %v", 113 node.nodeID, 114 node.transactionID, 115 node.prevTransactionID, 116 ), 117 ) 118 } else { 119 node = prevNode 120 nodes = append(nodes, node) 121 } 122 } 123 124 // now node should be the first node 125 // node.nodeID == common.FirstEventID 126 // node.prevTransactionID == 0 127 if node.nodeID != common.FirstEventID || node.prevTransactionID != 0 { 128 return nil, serviceerror.NewInternal( 129 fmt.Sprintf("unable to back trace history node, node ID: %v, transaction ID: %v, prev transaction ID: %v", 130 node.nodeID, 131 node.transactionID, 132 node.prevTransactionID, 133 ), 134 ) 135 } 136 137 return nodes, nil 138 } 139 140 func trimNodes( 141 nodeIDToNodes map[int64][]historyNodeMetadata, 142 nodeChain []historyNodeMetadata, 143 ) []historyNodeMetadata { 144 var nodesToTrim []historyNodeMetadata 145 // for each node on the chain, validate that the transaction ID being the largest 146 for _, validNode := range nodeChain { 147 for _, node := range nodeIDToNodes[validNode.nodeID] { 148 if node.transactionID <= validNode.transactionID { 149 break 150 } 151 nodesToTrim = append(nodesToTrim, node) 152 } 153 } 154 return nodesToTrim 155 }