feat: reconcile feedback fail closed

This commit is contained in:
zgw
2026-07-30 13:46:33 +08:00
parent cdbb59ca65
commit 63071fab6f
4 changed files with 217 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
package com.ft546.wcs.shadow.feedback;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public final class ExecutionFeedback {
private final String plcBootId;
private final long plannerEpoch;
private final long planVersion;
private final long segmentSeq;
private final long snapshotSeq;
private final String currentSite;
private final List<String> remainingPath;
private final boolean connected;
private final boolean commitAckKnown;
public ExecutionFeedback(String plcBootId, long plannerEpoch, long planVersion, long segmentSeq,
long snapshotSeq, String currentSite, List<String> remainingPath,
boolean connected, boolean commitAckKnown) {
this.plcBootId = plcBootId;
this.plannerEpoch = plannerEpoch;
this.planVersion = planVersion;
this.segmentSeq = segmentSeq;
this.snapshotSeq = snapshotSeq;
this.currentSite = currentSite;
this.remainingPath = Collections.unmodifiableList(new ArrayList<String>(remainingPath));
this.connected = connected;
this.commitAckKnown = commitAckKnown;
}
public String getPlcBootId() { return plcBootId; }
public long getPlannerEpoch() { return plannerEpoch; }
public long getPlanVersion() { return planVersion; }
public long getSegmentSeq() { return segmentSeq; }
public long getSnapshotSeq() { return snapshotSeq; }
public String getCurrentSite() { return currentSite; }
public List<String> getRemainingPath() { return remainingPath; }
public boolean isConnected() { return connected; }
public boolean isCommitAckKnown() { return commitAckKnown; }
}

View File

@@ -0,0 +1,60 @@
package com.ft546.wcs.shadow.feedback;
import com.ft546.wcs.shadow.resource.ResourceKey;
import com.ft546.wcs.shadow.segment.SegmentEvent;
import com.ft546.wcs.shadow.segment.TrafficSegment;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public final class FeedbackReconciler {
public FeedbackResult reconcile(TrafficSegment segment, ExecutionFeedback feedback) {
if (!feedback.isConnected()) return quarantine(segment, "COMMUNICATION_LOST");
if (!feedback.isCommitAckKnown()) return quarantine(segment, "COMMIT_ACK_UNKNOWN");
if (!segment.getPlcBootId().equals(feedback.getPlcBootId())) {
return quarantine(segment, "PLC_RESTARTED");
}
if (segment.getPlannerEpoch() != feedback.getPlannerEpoch()
|| segment.getPlanVersion() != feedback.getPlanVersion()
|| segment.getSegmentSeq() != feedback.getSegmentSeq()) {
return quarantine(segment, "VERSION_MISMATCH");
}
if (feedback.getSnapshotSeq() <= segment.getLastSnapshotSeq()) {
return new FeedbackResult(false, "DUPLICATE_SNAPSHOT",
Collections.<ResourceKey>emptySet(), allResources(segment.getPath()), segment);
}
int currentIndex = segment.getPath().indexOf(feedback.getCurrentSite());
if (currentIndex < 0
|| !segment.getPath().subList(currentIndex, segment.getPath().size())
.equals(feedback.getRemainingPath())) {
return quarantine(segment, "PATH_MISMATCH");
}
Set<ResourceKey> released = new LinkedHashSet<ResourceKey>();
Set<ResourceKey> retained = new LinkedHashSet<ResourceKey>();
List<String> path = segment.getPath();
for (int i = 0; i < path.size(); i++) {
(i < currentIndex ? released : retained).add(ResourceKey.node(path.get(i)));
if (i < path.size() - 1) {
(i < currentIndex ? released : retained).add(ResourceKey.edge(path.get(i), path.get(i + 1)));
}
}
return new FeedbackResult(false, "MATCHED_PROGRESS", released, retained,
segment.withLastSnapshotSeq(feedback.getSnapshotSeq()));
}
private FeedbackResult quarantine(TrafficSegment segment, String reason) {
return new FeedbackResult(true, reason, Collections.<ResourceKey>emptySet(),
allResources(segment.getPath()), segment.transition(SegmentEvent.QUARANTINE));
}
private Set<ResourceKey> allResources(List<String> path) {
Set<ResourceKey> resources = new LinkedHashSet<ResourceKey>();
for (int i = 0; i < path.size(); i++) {
resources.add(ResourceKey.node(path.get(i)));
if (i < path.size() - 1) resources.add(ResourceKey.edge(path.get(i), path.get(i + 1)));
}
return resources;
}
}

View File

@@ -0,0 +1,33 @@
package com.ft546.wcs.shadow.feedback;
import com.ft546.wcs.shadow.resource.ResourceKey;
import com.ft546.wcs.shadow.segment.TrafficSegment;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
public final class FeedbackResult {
private final boolean quarantined;
private final String reasonCode;
private final Set<ResourceKey> releasedResources;
private final Set<ResourceKey> retainedResources;
private final TrafficSegment updatedSegment;
FeedbackResult(boolean quarantined, String reasonCode, Set<ResourceKey> releasedResources,
Set<ResourceKey> retainedResources, TrafficSegment updatedSegment) {
this.quarantined = quarantined;
this.reasonCode = reasonCode;
this.releasedResources = Collections.unmodifiableSet(
new LinkedHashSet<ResourceKey>(releasedResources));
this.retainedResources = Collections.unmodifiableSet(
new LinkedHashSet<ResourceKey>(retainedResources));
this.updatedSegment = updatedSegment;
}
public boolean isQuarantined() { return quarantined; }
public String getReasonCode() { return reasonCode; }
public Set<ResourceKey> getReleasedResources() { return releasedResources; }
public Set<ResourceKey> getRetainedResources() { return retainedResources; }
public TrafficSegment getUpdatedSegment() { return updatedSegment; }
}