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  }