6
6
* | __/\ V /| | || (_| | |_| | |_) |
7
7
* \___| \_/ |_|\__\__,_|____/|____/
8
8
*
9
- * Copyright (c) 2023-2024
9
+ * Copyright (c) 2023-2025
10
10
*
11
11
* Licensed under the Business Source License, Version 1.1 (the "License");
12
12
* you may not use this file except in compliance with the License.
@@ -303,7 +303,7 @@ public ConsistencyReport getConsistencyReport() {
303
303
int previousElementId = headElementId ;
304
304
for (int i = 0 ; i < unorderedList .length ; i ++) {
305
305
int elementId = unorderedList [i ];
306
- final ChainElementState state = elementStates .get (elementId );
306
+ final ChainElementState state = this . elementStates .get (elementId );
307
307
if (state == null ) {
308
308
errors .append ("\n The element `" )
309
309
.append (elementId )
@@ -594,7 +594,7 @@ private OptionalInt removeSuccessorElement(int primaryKey, int chainHeadPk) {
594
594
reclassifyChain (subChainWithoutRemovedElement [0 ], subChainWithoutRemovedElement );
595
595
596
596
// verify whether the head of chain was not in circular conflict and if so
597
- verifyIfCircularDependencyExistsAndIsBroken (existingStateHeadState );
597
+ verifyIfCircularDependencyExistsAndIsBroken (chainHeadPk , existingStateHeadState );
598
598
} else {
599
599
// just remove it from the chain
600
600
chain .removeRange (index , chain .getLength ());
@@ -677,20 +677,21 @@ private void introduceNewSuccessorChain(int primaryKey, @Nonnull ChainableType p
677
677
*/
678
678
private void updatePredecessor (int primaryKey , @ Nonnull ChainableType predecessor , @ Nonnull ChainElementState existingState ) {
679
679
// the primary key was already present in the index - we need to relocate it
680
- final TransactionalUnorderedIntArray existingChain = this .chains .get (existingState .inChainOfHeadWithPrimaryKey ());
680
+ final int primaryKeyOfExistingHeadState = existingState .inChainOfHeadWithPrimaryKey ();
681
+ final TransactionalUnorderedIntArray existingChain = this .chains .get (primaryKeyOfExistingHeadState );
681
682
final int index = existingChain .indexOf (primaryKey );
682
683
// sanity check - the primary key must be present in the chain according to the state information
683
684
Assert .isPremiseValid (
684
685
index >= 0 ,
685
686
"Index damaged! The primary key `" + primaryKey + "` must be present in the chain according to the state information!"
686
687
);
687
688
// we need to remember original state of the chain head before changes in order to resolve possible circular dependency
688
- final ChainElementState existingStateHeadState = this .elementStates .get (existingState . inChainOfHeadWithPrimaryKey () );
689
+ final ChainElementState existingStateHeadState = this .elementStates .get (primaryKeyOfExistingHeadState );
689
690
// if newly created element is a head of its chain
690
691
if (predecessor .isHead ()) {
691
- updateElementToBecomeHeadOfTheChain (primaryKey , index , predecessor , existingChain , existingStateHeadState );
692
+ updateElementToBecomeHeadOfTheChain (primaryKey , index , predecessor , existingChain , primaryKeyOfExistingHeadState , existingStateHeadState );
692
693
} else {
693
- updateElementWithinExistingChain (primaryKey , index , predecessor , existingChain , existingStateHeadState , existingState );
694
+ updateElementWithinExistingChain (primaryKey , index , predecessor , existingChain , primaryKeyOfExistingHeadState , existingStateHeadState , existingState );
694
695
}
695
696
}
696
697
@@ -701,13 +702,15 @@ private void updatePredecessor(int primaryKey, @Nonnull ChainableType predecesso
701
702
* @param index index of the element in the chain
702
703
* @param predecessor pointer record to a predecessor element of the `primaryKey` element
703
704
* @param existingChain existing chain where the element is present
704
- * @param existingStateHeadState state of the head element of the existing chain where element is present
705
+ * @param primaryKeyOfExistingHeadState primary key the `existingHeadState` is registered in `this.elementStates`
706
+ * @param existingHeadState state of the head element of the existing chain where element is present
705
707
*/
706
708
private void updateElementToBecomeHeadOfTheChain (
707
709
int primaryKey ,
708
710
int index , @ Nonnull ChainableType predecessor ,
709
711
@ Nonnull TransactionalUnorderedIntArray existingChain ,
710
- @ Nonnull ChainElementState existingStateHeadState
712
+ int primaryKeyOfExistingHeadState ,
713
+ @ Nonnull ChainElementState existingHeadState
711
714
) {
712
715
// if the primary key is not located at the head of the chain
713
716
if (index > 0 ) {
@@ -717,7 +720,7 @@ private void updateElementToBecomeHeadOfTheChain(
717
720
this .chains .put (primaryKey , new TransactionalUnorderedIntArray (subChain ));
718
721
reclassifyChain (primaryKey , subChain );
719
722
// we need to re-check whether the original chain has still circular dependency when this chain was split
720
- verifyIfCircularDependencyExistsAndIsBroken (existingStateHeadState );
723
+ verifyIfCircularDependencyExistsAndIsBroken (primaryKeyOfExistingHeadState , existingHeadState );
721
724
} else {
722
725
// we just need to change the state, since the chain is already present
723
726
}
@@ -736,6 +739,7 @@ private void updateElementToBecomeHeadOfTheChain(
736
739
* @param index index of the element in the chain
737
740
* @param predecessor pointer record to a predecessor element of the `primaryKey` element
738
741
* @param existingChain existing chain where the element is present
742
+ * @param primaryKeyOfExistingHeadState primary key the `existingHeadState` is registered in `this.elementStates`
739
743
* @param existingStateHeadState state of the head element of the existing chain where element is present
740
744
* @param existingState existing state of the primary key element
741
745
*/
@@ -744,6 +748,7 @@ private void updateElementWithinExistingChain(
744
748
int index ,
745
749
@ Nonnull ChainableType predecessor ,
746
750
@ Nonnull TransactionalUnorderedIntArray existingChain ,
751
+ int primaryKeyOfExistingHeadState ,
747
752
@ Nonnull ChainElementState existingStateHeadState ,
748
753
@ Nonnull ChainElementState existingState
749
754
) {
@@ -752,7 +757,7 @@ private void updateElementWithinExistingChain(
752
757
// if there is circular conflict - set up a separate chain and update state
753
758
if (circularConflict ) {
754
759
updateElementWithCircularConflict (
755
- primaryKey , index , predecessor , existingChain , existingStateHeadState , existingState
760
+ primaryKey , index , predecessor , existingChain , primaryKeyOfExistingHeadState , existingStateHeadState , existingState
756
761
);
757
762
} else {
758
763
final int [] movedChain ;
@@ -837,7 +842,7 @@ private void updateElementWithinExistingChain(
837
842
}
838
843
839
844
// verify whether the head of chain was not in circular conflict and if so
840
- verifyIfCircularDependencyExistsAndIsBroken (existingStateHeadState );
845
+ verifyIfCircularDependencyExistsAndIsBroken (primaryKeyOfExistingHeadState , existingStateHeadState );
841
846
}
842
847
}
843
848
@@ -867,6 +872,7 @@ private void examineCircularConflictInNewlyAppendedChain(int chainHeadPrimaryKey
867
872
* @param index index of the element in the chain
868
873
* @param predecessor pointer record to a predecessor element of the `primaryKey` element
869
874
* @param existingChain existing chain where the element is present
875
+ * @param primaryKeyOfExistingHeadState primary key the `existingHeadState` is registered in `this.elementStates`
870
876
* @param existingStateHeadState state of the head element of the existing chain where element is present
871
877
* @param existingState existing state of the primary key element
872
878
*/
@@ -875,6 +881,7 @@ private void updateElementWithCircularConflict(
875
881
int index ,
876
882
@ Nonnull ChainableType predecessor ,
877
883
@ Nonnull TransactionalUnorderedIntArray existingChain ,
884
+ int primaryKeyOfExistingHeadState ,
878
885
@ Nonnull ChainElementState existingStateHeadState ,
879
886
@ Nonnull ChainElementState existingState
880
887
) {
@@ -888,7 +895,7 @@ private void updateElementWithCircularConflict(
888
895
this .chains .put (primaryKey , new TransactionalUnorderedIntArray (subChain ));
889
896
reclassifyChain (primaryKey , subChain );
890
897
// we need to re-check whether the original chain has still circular dependency when this chain was split
891
- verifyIfCircularDependencyExistsAndIsBroken (existingStateHeadState );
898
+ verifyIfCircularDependencyExistsAndIsBroken (primaryKeyOfExistingHeadState , existingStateHeadState );
892
899
}
893
900
}
894
901
@@ -897,28 +904,35 @@ private void updateElementWithCircularConflict(
897
904
* If so, the method checks whether the circular dependency still exists for the current state of the chain and
898
905
* if not, it is resolved.
899
906
*
907
+ * @param primaryKeyOfHeadState primary key that was used to get the state from `this.elementStates` for verification
900
908
* @param originalChainHeadState the state of the original chain head before the change
901
909
*/
902
910
private void verifyIfCircularDependencyExistsAndIsBroken (
911
+ int primaryKeyOfHeadState ,
903
912
@ Nonnull ChainElementState originalChainHeadState
904
913
) {
905
914
// if original chain head is in circular dependency
906
915
if (originalChainHeadState .state () == ElementState .CIRCULAR ) {
916
+ Assert .isPremiseValid (
917
+ primaryKeyOfHeadState == originalChainHeadState .inChainOfHeadWithPrimaryKey (),
918
+ "Primary key of the state must match the primary key of the state chain head!"
919
+ );
920
+
907
921
final TransactionalUnorderedIntArray originalHeadChain = this .chains .get (originalChainHeadState .inChainOfHeadWithPrimaryKey ());
908
922
if (originalHeadChain != null ) {
909
923
// verify it is still in circular dependency
910
924
if (originalHeadChain .indexOf (originalChainHeadState .predecessorPrimaryKey ()) < 0 ) {
911
925
// the circular dependency was broken
912
926
this .elementStates .put (
913
- originalChainHeadState . inChainOfHeadWithPrimaryKey () ,
927
+ primaryKeyOfHeadState ,
914
928
new ChainElementState (originalChainHeadState , ElementState .SUCCESSOR )
915
929
);
916
930
}
917
931
} else {
918
932
// the circular dependency was broken - the chain is now part of another chain
919
933
this .elementStates .compute (
920
- originalChainHeadState . inChainOfHeadWithPrimaryKey () ,
921
- (k , newState ) -> new ChainElementState (newState , ElementState .SUCCESSOR )
934
+ primaryKeyOfHeadState ,
935
+ (k , existingState ) -> new ChainElementState (existingState , ElementState .SUCCESSOR )
922
936
);
923
937
}
924
938
}
@@ -966,7 +980,7 @@ private Integer attemptToCollapseChain(int element) {
966
980
if (chainHeadState .state () == ElementState .SUCCESSOR ) {
967
981
return mergeSuccessorChainToElementChainIfPossible (chainHeadState );
968
982
} else if (chainHeadState .state == ElementState .HEAD ) {
969
- return findFirstSuccessorChainAndMergeToElementChain (chainHeadState );
983
+ return findFirstSuccessorChainAndMergeToElementChain (chainHeadState . inChainOfHeadWithPrimaryKey () );
970
984
} else {
971
985
return null ;
972
986
}
@@ -977,14 +991,16 @@ private Integer attemptToCollapseChain(int element) {
977
991
* the predecessor. In such case, the chain can be merged with this chain. The chain is then merged with it and
978
992
* the method returns the primary key of the new head of the chain.
979
993
*
980
- * If this primary lookup fails, the {@link #findFirstSuccessorChainAndMergeToElementChain(ChainElementState )}
994
+ * If this primary lookup fails, the {@link #findFirstSuccessorChainAndMergeToElementChain(int )}
981
995
* method is called to verify whether we can't collapse another chain with tail element of the chain.
982
996
*
983
997
* @param chainHeadState state of the element pointing to chain which head element we check for predecessor chain
984
998
* @return primary key of the new head of the chain or NULL if the chain can't be collapsed
985
999
*/
986
1000
@ Nullable
987
- private Integer mergeSuccessorChainToElementChainIfPossible (@ Nonnull ChainElementState chainHeadState ) {
1001
+ private Integer mergeSuccessorChainToElementChainIfPossible (
1002
+ @ Nonnull ChainElementState chainHeadState
1003
+ ) {
988
1004
final ChainElementState predecessorState = this .elementStates .get (chainHeadState .predecessorPrimaryKey ());
989
1005
// predecessor may not yet be present in the index
990
1006
if (predecessorState != null ) {
@@ -1002,20 +1018,19 @@ private Integer mergeSuccessorChainToElementChainIfPossible(@Nonnull ChainElemen
1002
1018
return predecessorState .inChainOfHeadWithPrimaryKey ();
1003
1019
}
1004
1020
}
1005
- return findFirstSuccessorChainAndMergeToElementChain (chainHeadState );
1021
+ return findFirstSuccessorChainAndMergeToElementChain (chainHeadState . inChainOfHeadWithPrimaryKey () );
1006
1022
}
1007
1023
1008
1024
/**
1009
1025
* Method checks whether there is a chain that is marked as a SUCCESSOR of a last element of the chain belonging
1010
1026
* to `chainHeadState`. If so, the SUCCESSOR chain is fully merged with the chain of `chainHeadState` and the
1011
1027
* method returns the primary key of the new head of the chain.
1012
1028
*
1013
- * @param chainHeadState state of the element pointing to chain for which we are looking for SUCCESSOR chain
1029
+ * @param chainHeadElement the primary key of head of the chain to which we check for successor chain
1014
1030
* @return primary key of the new head of the chain or NULL if the chain can't be collapsed
1015
1031
*/
1016
1032
@ Nullable
1017
- private Integer findFirstSuccessorChainAndMergeToElementChain (@ Nonnull ChainElementState chainHeadState ) {
1018
- final int chainHeadElement = chainHeadState .inChainOfHeadWithPrimaryKey ();
1033
+ private Integer findFirstSuccessorChainAndMergeToElementChain (int chainHeadElement ) {
1019
1034
final TransactionalUnorderedIntArray chain = this .chains .get (chainHeadElement );
1020
1035
final int lastRecordId = chain .getLastRecordId ();
1021
1036
final Optional <Integer > collapsableChain = this .chains .keySet ()
@@ -1048,7 +1063,7 @@ private Integer findFirstSuccessorChainAndMergeToElementChain(@Nonnull ChainElem
1048
1063
chainHeadElement ,
1049
1064
new ChainElementState (
1050
1065
chainHeadElement ,
1051
- chainHeadState .predecessorPrimaryKey (),
1066
+ chainHeadElementState .predecessorPrimaryKey (),
1052
1067
ElementState .CIRCULAR
1053
1068
)
1054
1069
);
0 commit comments