Batch Close Protocol

Пачку параллельной работы закрывают не тем, что дождались всех, а протоколом: заморозить допуск нового, дать доиграть начатому, свести в фиксированном порядке и поставить единственный маркер завершения. Без маркера «пачка закрыта» определяется по наличию файлов в хранилище — а это же состояние достижимо после падения на середине сведения.

Чем плохо «дождались всех»

Веер задач разослан, результаты собираются, дальше их надо свести и запустить следующий круг. Кажется, что закрытие — это ожидание последнего исполнителя. Отказы, которые из такого закрытия следуют, все молчаливые:

  • Наполовину сведённая пачка неотличима от сведённой. Сведение — это несколько записей подряд; падение между ними оставляет хранилище в состоянии, где часть результатов учтена, часть нет, и ни одного признака этого нет.
  • Поздний результат въезжает в закрытую пачку. Медленный исполнитель публикует свой результат уже после сведения; повтор операции его подхватывает, и та же пачка при двух прогонах даёт разный состав.
  • Остановка по таймеру убивает оплаченную работу. Жёсткое завершение по времени обрывает исполнителей, которые уже почти закончили, — деньги потрачены, результата нет.
  • Работа, не доехавшая до публикации, влияет невидимо. Исполнитель оставил файлы в рабочей области, но не опубликовал результат; следующий круг эти файлы находит и считает их чем-то значащим.

Заморозка допуска — не то же самое, что остановка

Первый шаг закрытия — перестать принимать новое, не трогая начатое. Это два разных сигнала, и путать их дорого:

  • заморозка допуска блокирует постановку новых задач, запуск процессов и любые дорогие операции, но оставляет разрешёнными чтение результатов, публикацию найденного и обновление памяти — то есть ровно то, чем исполнитель завершает уже сделанное;
  • остановка приходит позже и после ограниченной паузы, за которую начатое успевает опубликоваться.

Отдельное правило внутри: активную защищённую работу не убивают. Что считать защищённым, решает задача — обычно это прогон, результат которого и есть цель, в отличие от вспомогательных вычислений.

Маркер — единственный факт завершения

Наличие результатов, обновлённой сводки или отчёта завершением не является: это следы, которые остаются и от прерванного сведения. Факт завершения — отдельная запись, которую ставят последней и по которой все остальные читатели узнают, что пачка закрыта.

Отсюда правило возобновления: результаты без маркера — это незавершённая работа по сведению, а не готовая пачка, и возобновление обязано её доделать, а не пропустить. Тот же принцип с другой стороны разобран в Unknown Not A Value: побочный след — не факт, и вывод команды не сообщает о её завершении.

Отсечка по времени делает повтор детерминированным

Вместе с маркером записывают момент отсечки: что было видно до него — попадает в пачку, что появилось после — не попадает, даже если операцию сведения повторить.

Без отсечки повтор недетерминирован по построению: он видит хранилище в момент повтора, а не в момент закрытия. С отсечкой поздний результат не пропадает — он остаётся видимым как сигнал для следующего круга, но не переписывает уже закрытое.

Порядок сведения фиксирован, а маркер ставится последним

Сведение — не «слить всё», а последовательность с одним направлением зависимости:

  1. собрать опубликованное исполнителями;
  2. привести к каноническому виду идемпотентно — повтор шага не должен менять результат (Idempotency For Agents);
  3. обновить каноническое состояние: то, что следующий круг считает истиной;
  4. обновить производное: сводки, рейтинги, память исполнителей;
  5. спланировать следующий круг по уже закоммиченному, а не по тому, что пришло в этот момент;
  6. поставить маркер.

Шаг 5 стоит после 3 намеренно: планирование, читающее незакоммиченное, делает следующий круг зависящим от порядка прихода результатов.

Что не доехало до публикации, не влияет

Полезное правило распространения: следующий круг видит только опубликованное. Артефакт, оставшийся в рабочей области без публикации, не участвует в планировании, даже если он лежит на видном месте.

Смысл в разделении «сделано» и «сообщено». Файл в рабочей области — побочный эффект работы, а не её результат; результатом он становится тогда, когда исполнитель его опубликовал вместе с оговорками и указанием, как его читать. Иначе следующий круг наследует чужие черновики, приняв их за выводы.

Закрывать по достаточности, а не по числу закончивших

Последнее решение — когда вообще начинать закрытие. «Все закончили» плохой критерий: одна застрявшая ветка держит пачку неограниченно, а быстрое большинство ничего не решает.

Работающая форма — доля годного к закрытию: пачка закрывается, когда набралась заданная доля результатов, доросших до нужной зрелости. Дальше три случая, и все три предусматриваются заранее:

  • доля набрана — заморозить допуск, дать доиграть, свести, закрыть;
  • доля не набрана к моменту оценки — перестать принимать обычную работу, но оставить допуск тем задачам, которые ещё успевают дать годный результат к сроку;
  • упёрлись в предохранитель по времени — закрыться, записав, что зрелости не хватило. Это важнее, чем кажется: закрытие с недобором обязано оставлять след, иначе следующий круг унаследует слабую границу как нормальную.

Связано с

  • Multi Agent Patterns — веер задач и координация через общее состояние; здесь его вторая половина, сведение
  • Cohort Diversity — что запускать в пачку, чтобы она не оказалась одной задачей в N копиях
  • Durable Execution — возобновление после падения; маркер и отсечка делают его детерминированным
  • Idempotency For Agents — почему шаг приведения к каноническому виду обязан быть повторяемым
  • Unknown Not A Value — побочный след не является фактом завершения
  • Agent Audit Log — маркер и отсечка как то, по чему потом восстанавливают, что вошло в решение
  • Parallel Agent Dispatch — цена ожидания и то, почему остановка по таймеру дороже дренажа