go.temporal.io/server@v1.23.0/common/persistence/versionhistory/version_history.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 versionhistory
    26  
    27  import (
    28  	"fmt"
    29  
    30  	"go.temporal.io/api/serviceerror"
    31  
    32  	historyspb "go.temporal.io/server/api/history/v1"
    33  	"go.temporal.io/server/common"
    34  )
    35  
    36  // NewVersionHistory create a new instance of VersionHistory.
    37  func NewVersionHistory(branchToken []byte, items []*historyspb.VersionHistoryItem) *historyspb.VersionHistory {
    38  	return &historyspb.VersionHistory{
    39  		BranchToken: branchToken,
    40  		Items:       items,
    41  	}
    42  }
    43  
    44  // CopyVersionHistory copies VersionHistory.
    45  func CopyVersionHistory(v *historyspb.VersionHistory) *historyspb.VersionHistory {
    46  	token := make([]byte, len(v.BranchToken))
    47  	copy(token, v.BranchToken)
    48  
    49  	var items []*historyspb.VersionHistoryItem
    50  	for _, item := range v.Items {
    51  		items = append(items, CopyVersionHistoryItem(item))
    52  	}
    53  
    54  	return NewVersionHistory(token, items)
    55  }
    56  
    57  // CopyVersionHistoryUntilLCAVersionHistoryItem returns copy of VersionHistory up until LCA item.
    58  func CopyVersionHistoryUntilLCAVersionHistoryItem(v *historyspb.VersionHistory, lcaItem *historyspb.VersionHistoryItem) (*historyspb.VersionHistory, error) {
    59  	versionHistory := &historyspb.VersionHistory{}
    60  	notFoundErr := serviceerror.NewInternal("version history does not contains the LCA item.")
    61  	for _, item := range v.Items {
    62  		if item.Version < lcaItem.Version {
    63  			if err := AddOrUpdateVersionHistoryItem(versionHistory, item); err != nil {
    64  				return nil, err
    65  			}
    66  		} else if item.Version == lcaItem.Version {
    67  			if lcaItem.GetEventId() > item.GetEventId() {
    68  				return nil, notFoundErr
    69  			}
    70  			if err := AddOrUpdateVersionHistoryItem(versionHistory, lcaItem); err != nil {
    71  				return nil, err
    72  			}
    73  			return versionHistory, nil
    74  		} else {
    75  			return nil, notFoundErr
    76  		}
    77  	}
    78  	return nil, notFoundErr
    79  }
    80  
    81  // SetVersionHistoryBranchToken sets the branch token.
    82  func SetVersionHistoryBranchToken(v *historyspb.VersionHistory, branchToken []byte) {
    83  	v.BranchToken = make([]byte, len(branchToken))
    84  	copy(v.BranchToken, branchToken)
    85  }
    86  
    87  // AddOrUpdateVersionHistoryItem updates the VersionHistory with new VersionHistoryItem.
    88  func AddOrUpdateVersionHistoryItem(v *historyspb.VersionHistory, item *historyspb.VersionHistoryItem) error {
    89  	if len(v.Items) == 0 {
    90  		v.Items = []*historyspb.VersionHistoryItem{CopyVersionHistoryItem(item)}
    91  		return nil
    92  	}
    93  
    94  	lastItem := v.Items[len(v.Items)-1]
    95  	if item.Version < lastItem.Version {
    96  		return serviceerror.NewInternal(fmt.Sprintf("cannot update version history with a lower version %v. Last version: %v", item.Version, lastItem.Version))
    97  	}
    98  
    99  	if item.GetEventId() <= lastItem.GetEventId() {
   100  		return serviceerror.NewInternal(fmt.Sprintf("cannot add version history with a lower event id %v. Last event id: %v", item.GetEventId(), lastItem.GetEventId()))
   101  	}
   102  
   103  	if item.Version > lastItem.Version {
   104  		// Add a new history
   105  		v.Items = append(v.Items, CopyVersionHistoryItem(item))
   106  	} else {
   107  		// item.Version == lastItem.Version && item.EventID > lastItem.EventID
   108  		// Update event ID
   109  		lastItem.EventId = item.GetEventId()
   110  	}
   111  	return nil
   112  }
   113  
   114  // ContainsVersionHistoryItem check whether VersionHistory has given VersionHistoryItem.
   115  func ContainsVersionHistoryItem(v *historyspb.VersionHistory, item *historyspb.VersionHistoryItem) bool {
   116  	prevEventID := common.FirstEventID - 1
   117  	for _, currentItem := range v.Items {
   118  		if item.GetVersion() == currentItem.GetVersion() {
   119  			if prevEventID < item.GetEventId() && item.GetEventId() <= currentItem.GetEventId() {
   120  				return true
   121  			}
   122  		} else if item.GetVersion() < currentItem.GetVersion() {
   123  			return false
   124  		}
   125  		prevEventID = currentItem.GetEventId()
   126  	}
   127  	return false
   128  }
   129  
   130  // FindLCAVersionHistoryItem returns the lowest common ancestor VersionHistoryItem.
   131  func FindLCAVersionHistoryItem(v *historyspb.VersionHistory, remote *historyspb.VersionHistory) (*historyspb.VersionHistoryItem, error) {
   132  	return FindLCAVersionHistoryItemFromItemSlice(v.Items, remote.Items)
   133  }
   134  
   135  func FindLCAVersionHistoryItemFromItemSlice(versionHistoryItemsA []*historyspb.VersionHistoryItem, versionHistoryItemsB []*historyspb.VersionHistoryItem) (*historyspb.VersionHistoryItem, error) {
   136  	aIndex := len(versionHistoryItemsA) - 1
   137  	bIndex := len(versionHistoryItemsB) - 1
   138  
   139  	for aIndex >= 0 && bIndex >= 0 {
   140  		aVersionItem := versionHistoryItemsA[aIndex]
   141  		bVersionItem := versionHistoryItemsB[bIndex]
   142  
   143  		if aVersionItem.Version == bVersionItem.Version {
   144  			if aVersionItem.GetEventId() > bVersionItem.GetEventId() {
   145  				return CopyVersionHistoryItem(bVersionItem), nil
   146  			}
   147  			return aVersionItem, nil
   148  		} else if aVersionItem.Version > bVersionItem.Version {
   149  			aIndex--
   150  		} else {
   151  			// aVersionItem.Version < bVersionItem.Version
   152  			bIndex--
   153  		}
   154  	}
   155  
   156  	return nil, serviceerror.NewInternal("version history is malformed. No joint point found.")
   157  }
   158  
   159  // IsVersionHistoryItemsInSameBranch checks if two version history items are in the same branch
   160  func IsVersionHistoryItemsInSameBranch(versionHistoryItemsA []*historyspb.VersionHistoryItem, versionHistoryItemsB []*historyspb.VersionHistoryItem) bool {
   161  	lowestCommonAncestor, err := FindLCAVersionHistoryItemFromItemSlice(versionHistoryItemsA, versionHistoryItemsB)
   162  	if err != nil {
   163  		return false
   164  	}
   165  
   166  	aLastItem, err := getLastVersionHistoryItem(versionHistoryItemsA)
   167  	if err != nil {
   168  		return false
   169  	}
   170  
   171  	bLastItem, err := getLastVersionHistoryItem(versionHistoryItemsB)
   172  	if err != nil {
   173  		return false
   174  	}
   175  
   176  	return lowestCommonAncestor.Equal(aLastItem) || lowestCommonAncestor.Equal(bLastItem)
   177  }
   178  
   179  // IsLCAVersionHistoryItemAppendable checks if a LCA VersionHistoryItem is appendable.
   180  func IsLCAVersionHistoryItemAppendable(v *historyspb.VersionHistory, lcaItem *historyspb.VersionHistoryItem) bool {
   181  	if len(v.Items) == 0 {
   182  		panic("version history not initialized")
   183  	}
   184  	if lcaItem == nil {
   185  		panic("lcaItem is nil")
   186  	}
   187  
   188  	return IsEqualVersionHistoryItem(v.Items[len(v.Items)-1], lcaItem)
   189  }
   190  
   191  // GetFirstVersionHistoryItem return the first VersionHistoryItem.
   192  func GetFirstVersionHistoryItem(v *historyspb.VersionHistory) (*historyspb.VersionHistoryItem, error) {
   193  	if len(v.Items) == 0 {
   194  		return nil, serviceerror.NewInternal("version history is empty.")
   195  	}
   196  	return CopyVersionHistoryItem(v.Items[0]), nil
   197  }
   198  
   199  // GetLastVersionHistoryItem return the last VersionHistoryItem.
   200  func GetLastVersionHistoryItem(v *historyspb.VersionHistory) (*historyspb.VersionHistoryItem, error) {
   201  	return getLastVersionHistoryItem(v.Items)
   202  }
   203  
   204  func getLastVersionHistoryItem(v []*historyspb.VersionHistoryItem) (*historyspb.VersionHistoryItem, error) {
   205  	if len(v) == 0 {
   206  		return nil, serviceerror.NewInternal("version history is empty.")
   207  	}
   208  	return CopyVersionHistoryItem(v[len(v)-1]), nil
   209  }
   210  
   211  // GetVersionHistoryEventVersion return the corresponding event version of an event ID.
   212  func GetVersionHistoryEventVersion(v *historyspb.VersionHistory, eventID int64) (int64, error) {
   213  	lastItem, err := GetLastVersionHistoryItem(v)
   214  	if err != nil {
   215  		return 0, err
   216  	}
   217  	if eventID < common.FirstEventID || eventID > lastItem.GetEventId() {
   218  		return 0, serviceerror.NewInternal("input event ID is not in range.")
   219  	}
   220  
   221  	// items are sorted by eventID & version
   222  	// so the fist item with item event ID >= input event ID
   223  	// the item version is the result
   224  	for _, currentItem := range v.Items {
   225  		if eventID <= currentItem.GetEventId() {
   226  			return currentItem.GetVersion(), nil
   227  		}
   228  	}
   229  	return 0, serviceerror.NewInternal("input event ID is not in range.")
   230  }
   231  
   232  // IsEmptyVersionHistory indicate whether version history is empty
   233  func IsEmptyVersionHistory(v *historyspb.VersionHistory) bool {
   234  	return len(v.Items) == 0
   235  }
   236  
   237  // CompareVersionHistory compares 2 version history items
   238  func CompareVersionHistory(v1 *historyspb.VersionHistory, v2 *historyspb.VersionHistory) (int, error) {
   239  	lastItem1, err := GetLastVersionHistoryItem(v1)
   240  	if err != nil {
   241  		return 0, err
   242  	}
   243  	lastItem2, err := GetLastVersionHistoryItem(v2)
   244  	if err != nil {
   245  		return 0, err
   246  	}
   247  	return CompareVersionHistoryItem(lastItem1, lastItem2), nil
   248  }