По-видимому, это, вероятно, один из самых распространенных вопросов, которые задает новый разработчик Cadence. Рабочие процессы каденции должны быть детерминированными c алгоритмами . Если алгоритм рабочего процесса не детерминирован c, работники Cadence будут подвержены риску возникновения недетерминированных c рабочего процесса ошибок при попытке воспроизведения истории (ie. При восстановлении после сбоя работника).
Существует два способа решения этой проблемы:
- Создание нового рабочего процесса : это самый наивный подход для управления версиями рабочих процессов. Подход настолько прост, насколько это звучит: в любое время, когда вам нужно внести изменения в алгоритм вашего рабочего процесса, вы делаете копию своего исходного рабочего процесса и редактируете его так, как хотите, присваиваете ему новое имя, например MyWorkflow_V2, и начинаете использовать для всех новых экземпляры идут вперед. Если ваш рабочий процесс не очень продолжителен, ваши рабочие процессы в какой-то момент «истощатся», и вы сможете полностью удалить старую версию. С другой стороны, этот подход может очень быстро превратиться в кошмар обслуживания по очевидным причинам.
- Использование API GetVersion () для разветвления логи рабочего процесса c: у клиента Cadence есть функция с именем GetVersion, который сообщает вам, какая версия рабочего процесса запущена в данный момент. Вы можете использовать информацию, возвращаемую этой функцией, чтобы решить, какую версию алгоритма вашего рабочего процесса необходимо использовать. Другими словами, в вашем рабочем процессе работают как старый, так и новый алгоритмы, работающие бок о бок, и вы можете выбрать правильную версию для своих экземпляров рабочего процесса, чтобы обеспечить их детерминированную работу.
Ниже приведено пример подхода, основанного на GetVersion (). Предположим, вы хотите изменить следующую строку в вашем рабочем процессе:
err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
на
err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
Это серьезное изменение, так как вместо него выполняется действие bar Foo . Если вы просто внесете это изменение, не беспокоясь о детерминизме, ваши рабочие процессы не будут воспроизводиться, если они в этом нуждаются, и они застрянут с ошибкой nondeterministi c workflow . Правильный способ сделать это изменение правильно - это обновить рабочий процесс следующим образом:
v := GetVersion(ctx, "fooChange", DefaultVersion, 1)
if v == DefaultVersion {
err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
} else {
err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
}
Функция GetVersion принимает 4 параметра:
- ctx - стандарт объект контекста
- «fooChange» - это читаемый человеком ChangeID или семантичное c изменение, которое вы вносите в свой алгоритм рабочего процесса, который нарушает детерминизм
- DefaultVersion - это константа, которая просто означает Версия 0 . Другими словами, самая первая версия. Он передается как параметр minSupportedVersion в функцию GetVersion
- 1 - это maxSupportedVersion , который может обрабатываться текущим кодом рабочего процесса. В этом случае наш алгоритм может поддерживать версии рабочих процессов от DefaultVersion до Версия 1 (включительно).
Когда новый экземпляр этого рабочего процесса достигает GetVersion () Вызов выше в первый раз, функция вернет параметр maxSupportedVersion , чтобы вы могли запустить последнюю версию вашего алгоритма рабочего процесса. В то же время, он также запишет этот номер версии в историю рабочего процесса (внутренне известный как Marker Event ), чтобы он запомнился в будущем. При дальнейшем воспроизведении этого рабочего процесса клиент Cadence будет продолжать возвращать тот же номер версии, даже если вы передадите другой параметр maxSupportedVersion (ie. Если ваш рабочий процесс имеет еще больше версий).
Если вызов GetVersion обнаружен во время воспроизведения истории, и в истории нет события маркера, которое было зарегистрировано ранее, функция вернет DefaultVersion , при условии, что «FooChange» никогда не существовало в контексте этого экземпляра рабочего процесса.
В случае, если вам нужно сделать еще одно критическое изменение на том же шаге вашего рабочего процесса, вам просто нужно изменить приведенный выше код следующим образом:
v := GetVersion(ctx, "fooChange", DefaultVersion, 2) // Note the new max version
if v == DefaultVersion {
err = workflow.ExecuteActivity(ctx, foo).Get(ctx, nil)
} else if v == 1 {
err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
} else { // This is the Version 2 logic
err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil)
}
Когда вы можете отказаться от поддержки Версия 0 , вы изменяете приведенный выше код следующим образом:
v := GetVersion(ctx, "fooChange", 1, 2) // DefaultVersion is no longer supported
if v == 1 {
err = workflow.ExecuteActivity(ctx, bar).Get(ctx, nil)
} else {
err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil)
}
После этого изменения, если ваш код рабочего процесса выполняется для старого экземпляра рабочего процесса с DefaultVersion версия, клиент Cadence выдаст ошибку и остановит выполнение.
В конце концов, вы, вероятно, захотите избавиться от всех предыдущих версий и поддерживать только последнюю версию. Один из вариантов - просто избавиться от вызова GetVersion и оператора if в целом и просто иметь одну строку кода, которая делает правильные вещи. Однако на самом деле лучше оставить вызов GetVersion () по двум причинам:
- GetVersion () дает вам лучшее представление о том, что пошло не так, если ваш работник попытается воспроизвести историю старый экземпляр рабочего процесса. Вместо того чтобы исследовать причину root таинственной недетерминированной ошибки рабочего процесса c, вы будете знать, что причиной сбоя является управление версиями рабочего процесса в этом месте.
- Если вам нужно внести более серьезные изменения в на том же шаге алгоритма рабочего процесса вы сможете повторно использовать тот же идентификатор изменения и продолжить, следуя тому же шаблону, что и выше.
Учитывая две вышеупомянутые причины, вы должны обновить свой код рабочего процесса, подобный следующему, когда пришло время отказаться от поддержки всех старых версий:
GetVersion(ctx, "fooChange", 2, 2) // This acts like an assertion to give you a proper error
err = workflow.ExecuteActivity(ctx, baz).Get(ctx, nil)