Lösungen - AWS Flow Framework für Java

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Lösungen

Verwenden Sie eine der folgenden Lösungen, um abwärtsinkompatible Änderungen zu vermeiden. Weitere Informationen finden Sie unter Vornehmen von Änderungen am Entscheidercode und Beispielszenario.

Verwenden von Versioning

Für diese Lösung kopieren Sie den Entscheider in eine neue Klasse, modifizieren ihn und registrieren den Entscheider dann unter einer neuen Workflow-Version.

VersionedDecider.java

package sample.v2; import com.amazonaws.services.simpleworkflow.flow.DecisionContext; import com.amazonaws.services.simpleworkflow.flow.DecisionContextProviderImpl; import com.amazonaws.services.simpleworkflow.flow.WorkflowClock; import com.amazonaws.services.simpleworkflow.flow.annotations.Execute; import com.amazonaws.services.simpleworkflow.flow.annotations.Workflow; import com.amazonaws.services.simpleworkflow.flow.annotations.WorkflowRegistrationOptions; import sample.Input; @Workflow @WorkflowRegistrationOptions(defaultExecutionStartToCloseTimeoutSeconds = 60, defaultTaskStartToCloseTimeoutSeconds = 5) public interface Foo { @Execute(version = "2") public void sample(Input input); public static class Impl implements Foo { private DecisionContext decisionContext = new DecisionContextProviderImpl().getDecisionContext(); private WorkflowClock clock = decisionContext.getWorkflowClock(); @Override public void sample(Input input) { System.out.println("Decision (V2) WorkflowId: " + decisionContext.getWorkflowContext().getWorkflowExecution().getWorkflowId()); clock.createTimer(5); } } }

Im aktualisierten Java-Code führt der zweite Entscheiderauftragnehmer beide Versionen des Workflows aus. Dadurch können laufende Ausführungen unabhängig von den Änderungen in Version 2 fortgesetzt werden.

RunVersionedDecider.java

package sample; import com.amazonaws.services.simpleworkflow.flow.WorkflowWorker; public class VersionedChange extends SampleBase { public static void main(String[] args) throws Exception { new VersionedChange().run(); } public void run() throws Exception { // Start the first version of the decider, with workflow version 1 WorkflowWorker before = new WorkflowWorker(service, domain, taskList); before.addWorkflowImplementationType(sample.v1.Foo.Impl.class); before.start(); // Start a few executions with version 1 startFiveExecutions("Foo.sample", "1", new Input()); // Stop the first decider worker and wait a few seconds // for its pending pollers to match and return before.suspendPolling(); sleep(2000); // At this point, three executions are still open, with more decisions to make // Start a worker with both the previous version of the decider (workflow version 1) // and the modified code (workflow version 2) WorkflowWorker after = new WorkflowWorker(service, domain, taskList); after.addWorkflowImplementationType(sample.v1.Foo.Impl.class); after.addWorkflowImplementationType(sample.v2.Foo.Impl.class); after.start(); // Start a few more executions with version 2 startFiveExecutions("Foo.sample", "2", new Input()); printExecutionResults(); } }

Wenn Sie das Programm ausführen, werden alle Ausführungen erfolgreich abgeschlossen.

Verwenden von Funktions-Flags

Eine weitere Lösung für Probleme mit der Abwärtskompatibilität besteht darin, den Code basierend auf Eingabedaten anstelle von Workflow-Versionen in zwei Implementierungen in derselben Klasse aufzuteilen.

Wenn Sie diesen Ansatz wählen, fügen Sie Ihren Eingabeobjekten jedes Mal, wenn Sie sensible Änderungen vornehmen, Felder hinzu (oder modifizieren vorhandene Felder Ihrer Eingabeobjekte). Für Ausführungen, die vor der Migration beginnen, enthält das Eingabeobjekt das Feld nicht (oder es enthält einen anderen Wert). Daher müssen Sie die Versionsnummer nicht erhöhen.

Anmerkung

Wenn Sie neue Felder hinzufügen, stellen Sie sicher, dass der JSON-Deserialisierungsprozess abwärtskompatibel ist. Objekte, die vor der Einführung des Felds serialisiert wurden, sollten nach der Migration weiterhin erfolgreich deserialisiert werden können. Da JSON einen null-Wert festlegt, wenn ein Feld fehlt, verwenden Sie grundsätzlich gepackte Typen (Boolean anstelle von boolean) und verarbeiten Sie Fälle, in denen der Wert null ist.

FeatureFlagDecider.java

package sample.v1.featureflag; import com.amazonaws.services.simpleworkflow.flow.DecisionContext; import com.amazonaws.services.simpleworkflow.flow.DecisionContextProviderImpl; import com.amazonaws.services.simpleworkflow.flow.WorkflowClock; import com.amazonaws.services.simpleworkflow.flow.annotations.Execute; import com.amazonaws.services.simpleworkflow.flow.annotations.Workflow; import com.amazonaws.services.simpleworkflow.flow.annotations.WorkflowRegistrationOptions; import sample.Input; @Workflow @WorkflowRegistrationOptions(defaultExecutionStartToCloseTimeoutSeconds = 60, defaultTaskStartToCloseTimeoutSeconds = 5) public interface Foo { @Execute(version = "1") public void sample(Input input); public static class Impl implements Foo { private DecisionContext decisionContext = new DecisionContextProviderImpl().getDecisionContext(); private WorkflowClock clock = decisionContext.getWorkflowClock(); @Override public void sample(Input input) { System.out.println("Decision (V1 feature flag) WorkflowId: " + decisionContext.getWorkflowContext().getWorkflowExecution().getWorkflowId()); clock.createTimer(5); if (!input.getSkipSecondTimer()) { clock.createTimer(5); } } } }

Im aktualisierten Java-Code ist der Code beider Versionen des Workflows weiterhin für Version 1 registriert. Nach der Migration beginnen neue Ausführungen dagegen mit dem Feld skipSecondTimer der Eingabedaten mit dem Wert true.

RunFeatureFlagDecider.java

package sample; import com.amazonaws.services.simpleworkflow.flow.WorkflowWorker; public class FeatureFlagChange extends SampleBase { public static void main(String[] args) throws Exception { new FeatureFlagChange().run(); } public void run() throws Exception { // Start the first version of the decider WorkflowWorker before = new WorkflowWorker(service, domain, taskList); before.addWorkflowImplementationType(sample.v1.Foo.Impl.class); before.start(); // Start a few executions startFiveExecutions("Foo.sample", "1", new Input()); // Stop the first decider worker and wait a few seconds // for its pending pollers to match and return before.suspendPolling(); sleep(2000); // At this point, three executions are still open, with more decisions to make // Start a new version of the decider that introduces a change // while preserving backwards compatibility based on input fields WorkflowWorker after = new WorkflowWorker(service, domain, taskList); after.addWorkflowImplementationType(sample.v1.featureflag.Foo.Impl.class); after.start(); // Start a few more executions and enable the new feature through the input data startFiveExecutions("Foo.sample", "1", new Input().setSkipSecondTimer(true)); printExecutionResults(); } }

Wenn Sie das Programm ausführen, werden alle Ausführungen erfolgreich abgeschlossen.