sanpwc commented on code in PR #6850:
URL: https://github.com/apache/ignite-3/pull/6850#discussion_r2509287902


##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/core/NodeImpl.java:
##########
@@ -3635,6 +3673,16 @@ public void changePeersAndLearners(final Configuration 
newPeersAndLearners, long
                     return;
             }
 
+            if (this.conf.getConf().getSequenceToken() > 
newPeersAndLearners.getSequenceToken()) {
+                 LOG.info("Node {} received stale configuration for conf {}, 
existing is {}, new {}.",

Review Comment:
   According to our logging rules, we should put all params in [] at the end of 
the statement.
   Besides I'd also mention that given configuration will be ignored.



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/core/NodeImpl.java:
##########
@@ -3635,6 +3673,16 @@ public void changePeersAndLearners(final Configuration 
newPeersAndLearners, long
                     return;
             }
 
+            if (this.conf.getConf().getSequenceToken() > 
newPeersAndLearners.getSequenceToken()) {

Review Comment:
   Why it's '>' and not '>=' ?



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/core/NodeImpl.java:
##########
@@ -3635,6 +3673,16 @@ public void changePeersAndLearners(final Configuration 
newPeersAndLearners, long
                     return;
             }
 
+            if (this.conf.getConf().getSequenceToken() > 
newPeersAndLearners.getSequenceToken()) {
+                 LOG.info("Node {} received stale configuration for conf {}, 
existing is {}, new {}.",
+                        getNodeId(), newPeersAndLearners, 
this.conf.getConf().getSequenceToken(),  
newPeersAndLearners.getSequenceToken());
+                Status status = 
staleConfiguration(newPeersAndLearners.getSequenceToken());

Review Comment:
   Do we really need new raft error `RaftError.ESTALE `? How do you handle this 
error on the client side?



##########
modules/raft-api/src/main/java/org/apache/ignite/internal/raft/service/RaftGroupService.java:
##########
@@ -138,9 +142,11 @@ public interface RaftGroupService extends 
RaftCommandRunner {
      * @param peersAndLearners New peers and Learners of the Raft group.
      * @param term Current known leader term.
      *             If real raft group term will be different - configuration 
update will be skipped.
+     * @param sequenceToken Sequence token of the current change.
+     *
      * @return A future.
      */
-    CompletableFuture<Void> changePeersAndLearners(PeersAndLearners 
peersAndLearners, long term);
+    CompletableFuture<Void> changePeersAndLearners(PeersAndLearners 
peersAndLearners, long term, long sequenceToken);

Review Comment:
   I'd also mark term as deprecated for removal.



##########
modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftService.java:
##########
@@ -338,10 +338,12 @@ public CompletableFuture<Void> updateLearners(long term) {
 
         if (newLearners.isEmpty()) {
             // Methods for working with learners do not support empty peer 
lists for some reason.
-            return raftService.changePeersAndLearnersAsync(newConfiguration, 
term)
+            // TODO: https://issues.apache.org/jira/browse/IGNITE-26855.
+            return raftService.changePeersAndLearnersAsync(newConfiguration, 
term,  0)

Review Comment:
   Below I did suggest/ask why it's '>' and not '>=' in 
`this.conf.getConf().getSequenceToken() > newConf.getSequenceToken()`. In case 
of proposed substitution CMG/MG reconfigurations will break.



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/entity/LogEntry.java:
##########
@@ -45,6 +45,10 @@ public class LogEntry implements Checksum {
     /** true when the log has checksum **/
     private boolean hasChecksum;
 
+    private long sequenceToken;

Review Comment:
   Do we need sequenceToken in LogEntry in order to spread information about 
new applied configuration through raft group?



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/core/NodeImpl.java:
##########
@@ -553,16 +559,20 @@ void nextStage() {
                     LOG.info("Catch up phase to change peers was successfully 
finished "
                     + "[node={}, from peers={} to peers={}, from learners={}, 
to learners={}].",
                         this.node.getNodeId(), oldPeers, newPeers, 
oldLearners, newLearners);
-                    if (this.nchanges > 0) {
+                    if (this.nchanges > 0 || oldSequenceToken != 
sequenceToken) {

Review Comment:
   What if oldSequenceToken< sequenceToken? That seems invalid right, should we 
at least assert that?



##########
modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/network/replication/ChangePeersAndLearnersAsyncReplicaRequest.java:
##########
@@ -28,4 +28,6 @@
 public interface ChangePeersAndLearnersAsyncReplicaRequest extends 
PrimaryReplicaRequest {
     /** New peers configuration to rebalance. */
     byte[] pendingAssignments();
+
+    long sequenceToken();

Review Comment:
   I'd rather add a comment here explaining what it is, and why we need it. 



##########
modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/impl/raft/MetaStorageSnapshotStorageFactory.java:
##########
@@ -75,6 +75,8 @@ public MetaStorageSnapshotStorageFactory(KeyValueStorage 
storage) {
                 .peersList(configuration.peers())
                 .oldPeersList(configuration.oldPeers())
                 .learnersList(configuration.learners())
+                .sequenceToken(configuration.sequenceToken())
+                .oldSequenceToken(configuration.oldSequenceToken())

Review Comment:
   Here and there, why do we need oldSequenceToken?



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/core/NodeImpl.java:
##########
@@ -3701,11 +3768,16 @@ public Status resetPeers(final Configuration newPeers) {
                 LOG.warn("Node {} set peers need wait current conf changing.", 
getNodeId());
                 return new Status(RaftError.EBUSY, "Changing to another 
configuration");
             }
+
+            if (this.conf.getConf().getSequenceToken() > 
newPeers.getSequenceToken()) {
+                return staleConfiguration(newPeers.getSequenceToken());

Review Comment:
    I didn't get the point. Why in some cases we do log
   `LOG.info("Node {} received stale configuration for conf {}, existing is {}, 
new {}."` and in some cases don't?



##########
modules/raft-api/src/main/java/org/apache/ignite/internal/raft/RaftGroupConfiguration.java:
##########
@@ -40,6 +40,8 @@ public class RaftGroupConfiguration implements Serializable {
 
     private final long index;
     private final long term;
+    private final long sequenceToken;
+    private final long oldSequenceToken;

Review Comment:
   Why do we need `oldSequenceToken`?



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/conf/Configuration.java:
##########
@@ -318,4 +358,11 @@ public void diff(final Configuration rhs, final 
Configuration included, final Co
         excluded.peers = new ArrayList<>(rhs.peers);
         excluded.peers.removeAll(this.peers);
     }
+
+    /**
+     * Update the sequence token.
+     */
+    public void updateSequenceToken(long sequenceToken) {

Review Comment:
   Do we really need it? Currently it's used only within tests, right? So it's 
either @TestOnly, or even configuration should be immutable in the token 
context. 



##########
modules/replicator/src/main/java/org/apache/ignite/internal/replicator/ReplicaImpl.java:
##########
@@ -67,7 +68,7 @@ public class ReplicaImpl implements Replica {
 
     private final PlacementDriver placementDriver;
 
-    private final Function<ReplicationGroupId, CompletableFuture<byte[]>> 
getPendingAssignmentsSupplier;
+    private final Function<ReplicationGroupId, 
CompletableFuture<IgniteBiTuple<byte[], Long>>> getPendingAssignmentsSupplier;

Review Comment:
   IgniteBiTuple looks a bit ugly, I'd rather add some sort of wrapper.



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/entity/codec/v2/LogEntryV2CodecFactory.java:
##########
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.raft.jraft.entity.codec.v2;
+
+import org.apache.ignite.raft.jraft.entity.EnumOutter.EntryType;
+import org.apache.ignite.raft.jraft.entity.codec.LogEntryCodecFactory;
+import org.apache.ignite.raft.jraft.entity.codec.LogEntryDecoder;
+import org.apache.ignite.raft.jraft.entity.codec.LogEntryEncoder;
+
+/**
+ * Log entry codec implementation V2. Extends V1 format with sequence token 
support.
+ * Data format description:
+ * <ul>
+ *     <li>Magic header, 1 byte. {@link #MAGIC}</li>
+ *     <li>Log entry type, formally a var-long, effectively 1 byte. {@link 
EntryType}</li>
+ *     <li>Log index, var-long, from 1 to 9 bytes.</li>
+ *     <li>Log term, var-long, from 1 to 9 bytes.</li>
+ *     <li>Checksum, 8 bytes, Little Endian.</li>
+ *     <li><b>Sequence token, var-long, from 1 to 9 bytes. (NEW in V2)</b></li>
+ *     <li><b>Old sequence token, var-long, from 1 to 9 bytes. (NEW in 
V2)</b></li>
+ *     <li>If type is not {@link EntryType#ENTRY_TYPE_DATA}:<ul>
+ *         <li>Number of peers, var-long. Following it is the array of 
peers:</li>
+ *         <li>Length of the peer name, 2 bytes, Little Endian.</li>
+ *         <li>ASCII characters of the peer name, according to the read 
count.</li>
+ *         <li>Peer index, var-long.</li>
+ *         <li>Peer priority + 1, var-long.</li>
+ *         <li>... same block repeats for "oldPeers", "learners" and 
"oldLearners".</li>
+ *     </ul></li>
+ *     <li>If type is not {@link EntryType#ENTRY_TYPE_CONFIGURATION}:<ul>
+ *         <li>The rest of the {@code byte[]} is the data payload.</li>
+ *     </ul></li>
+ * </ul>
+ */
+public class LogEntryV2CodecFactory implements LogEntryCodecFactory {

Review Comment:
   It's not related to AI, however what will happen if oldNode will receive and 
a LogEntry v1? Do we have a tests for that?



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/rpc/impl/cli/AddLearnersRequestProcessor.java:
##########
@@ -60,9 +61,11 @@ protected Message processRequest0(final CliRequestContext 
ctx, final AddLearners
             addingLearners.add(peer);
         }
 
+        long sequenceToken = request.sequenceToken() != null ? 
request.sequenceToken() : Configuration.NO_SEQUENCE_TOKEN;

Review Comment:
   Why do we need this code?



##########
modules/raft-api/src/test/java/org/apache/ignite/internal/raft/RaftGroupConfigurationSerializerTest.java:
##########
@@ -88,5 +93,22 @@ void v2CanBeDeserialized() {
         assertThat(restoredConfig.learners(), is(List.of("learner1", 
"learner2")));
         assertThat(restoredConfig.oldPeers(), is(List.of("old-peer1", 
"old-peer2")));
         assertThat(restoredConfig.oldLearners(), is(List.of("old-learner1", 
"old-learner2")));
+        assertThat(restoredConfig.sequenceToken(), is(0L));

Review Comment:
   Somewhere, I've asked why we do need `oldSequenceToken`. If it'll occur that 
we need one, we should also `assertThat(restoredConfig.oldSequenceToken(), 
is(0L));`. Here and there.



##########
modules/raft/src/main/java/org/apache/ignite/raft/jraft/rpc/impl/cli/ChangePeersAndLearnersRequestProcessor.java:
##########
@@ -93,6 +95,7 @@ protected Message processRequest0(final CliRequestContext 
ctx, final ChangePeers
                     .newPeersList(toStringList(conf.getPeers()))
                     .oldLearnersList(toStringList(oldLearners))
                     .newLearnersList(toStringList(conf.getLearners()))
+                    .sequenceToken(sequenceToken)

Review Comment:
   We we do have sequenceToken in response? And why only in 
ChangePeersAndLearnersResponse?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to