Глубокое погружение в хуки | Workflow SDK

VVercel
Computing/SoftwareSmall Business/StartupsInternet Technology

Transcript

00:00:00Привет, большое спасибо, что присоединились к нам сегодня.
00:00:02Я Пранит из команды разработки воркфлоу в Vercel.
00:00:05Привет, я Нейт, тоже из команды воркфлоу.
00:00:08Нейт, мы с тобой в этой команде с самого начала,
00:00:12и из всего, что мы выпустили за последние шесть месяцев,
00:00:15хуки и вебхуки — одна из моих любимых функций,
00:00:18и именно об этом ты пришел сегодня рассказать.
00:00:21Хуки и вебхуки тоже моя любимая функция.
00:00:23Они невероятно мощные, и я покажу несколько демо, чтобы объяснить почему.
00:00:28Первое демо — это то, с чем мы все, вероятно, знакомы: магические ссылки.
00:00:33Магическая ссылка — это форма входа. Вы вводите свой email, получаете письмо,
00:00:40и когда вы нажимаете на ссылку, вы авторизуетесь в сервисе.
00:00:44Да, и если я правильно помню, в Vercel — еще до того, как он стал Vercel,
00:00:48когда он еще назывался Zeit, магические ссылки были единственным способом входа,
00:00:52и в то время мы сами построили всю эту систему.
00:00:56Верно, и у меня до сих пор ПТСР.
00:01:01Потому что без воркфлоу внедрение такой системы гораздо сложнее, чем кажется на первый взгляд.
00:01:08Логика оказывается разбросана по нескольким файлам.
00:01:12Нужно подключать базу данных для отслеживания состояния, и все быстро превращается в хаос.
00:01:19Да, я уже думал о том, как бы я это структурировал и какую базу данных использовал,
00:01:24потому что это похоже на классическую проблему, которую я решал раньше.
00:01:28Так что да, мне очень интересно посмотреть, как это выглядит.
00:01:30Да, чтобы продемонстрировать болевые точки, о которых я говорю,
00:01:38я начал с реализации «традиционной» версии магической ссылки без воркфлоу.
00:01:43Итак, здесь задействованы три эндпоинта.
00:01:47Первый — когда отправляется форма входа,
00:01:50он должен создать сессию и сохранить её в базе данных, например в Redis.
00:01:57Нужно реализовать TTL, нельзя оставлять данные висеть вечно, их нужно удалять.
00:02:06Затем отправка письма: она может сорваться, вход не сработает, и это будет неприятно.
00:02:14Верно, а потом вам нужен крон-джоб или стажер, чтобы чистить базу данных.
00:02:19Возможно, тем стажером в то время был я.
00:02:22Но есть второй эндпоинт — тот, который срабатывает, когда пользователь кликает по ссылке в письме.
00:02:28Он должен запросить базу данных и восстановить состояние, созданное в первом эндпоинте.
00:02:36И мы уже получаем настоящий «спагетти-код».
00:02:38Когда я представлял, как это будет выглядеть, этот код кажется таким знакомым, я бы и сам так сделал.
00:02:48Мы видим, что всё быстро усложняется, хотя концепция очень проста.
00:02:54Давайте посмотрим, как реализовать эту функцию с помощью Workflow.
00:02:59Реализация магической ссылки с использованием Workflow SDK выглядит примерно так.
00:03:05Мы видим нашу функцию с директивой useWorkflow, что означает, что это функция воркфлоу.
00:03:11И самое первое, что мы делаем — вызываем функцию createWebhook из пакета воркфлоу.
00:03:18Также мы используем опцию respondWithManual, что означает: наша функция воркфлоу будет отвечать за отправку HTTP-ответа на запрос к вебхуку.
00:03:36Это для того, чтобы можно было сделать редирект после входа?
00:03:40Да, если в функции воркфлоу есть информация, необходимая для определения типа ответа.
00:03:51Как и в первом эндпоинте, мы отправляем письмо для входа. Это функция useStep.
00:03:57Так что если что-то пойдет не так, Workflow SDK автоматически сделает повторные попытки.
00:04:03Аспект отказоустойчивости уже дает преимущество перед традиционным подходом.
00:04:10То есть sendLoginEmails — это шаг, и если отправка письма не удалась, вы повторяете её с тем же URL вебхука.
00:04:21И если мы посмотрим сюда, это очень интересный паттерн.
00:04:26Мы используем promise.race с ожиданием (sleep) в пять минут.
00:04:30Это возможно, потому что объект вебхука реализует промис.
00:04:35Чтобы дождаться запроса к этому вебхуку, вы просто пишете await Webhook.
00:04:40Или, как здесь, через race. Это круто, я ожидал, что у вебхука будет какой-то аргумент с таймаутом.
00:04:50Но мне нравится, что всё стало чище: чтобы сделать таймаут, вы просто моделируете гонку между вебхуком и ожиданием.
00:04:58Кажется, с этим можно сделать гораздо больше. Возможно, запустить гонку между двумя вебхуками.
00:05:02Мало что можно сделать, когда у тебя всего пара аргументов в функции.
00:05:06Но тот факт, что это просто промис и можно использовать promise.race против sleep или другого шага...
00:05:12Мне обожаю этот паттерн. Голова идет кругом от того, что можно на этом построить.
00:05:16Верно, в этом и прелесть примитивов, которые предлагает Workflow SDK.
00:05:21Все представлено в виде промисов.
00:05:23Так что стандартные паттерны JavaScript, такие как await promise.race, просто работают.
00:05:28И еще один момент: здесь нет Redis. Нет базы данных.
00:05:33В традиционном примере мы использовали TTL в Redis для реализации таймаута.
00:05:41А здесь мы используем примитив воркфлоу — sleep.
00:05:44И никакого стажера, который должен вычищать грязную базу данных после.
00:05:50Это лучшая часть.
00:05:51И вы видите, что воркфлоу отвечает на публичный запрос редиректом на страницу успешного входа.
00:05:59Затем он получает информацию о пользователе, чтобы вернуть её клиенту, инициировавшему вход.
00:06:07И это весь наш воркфлоу. Реализация магической ссылки заняла 50 строк кода.
00:06:12Невероятно. Можно увидеть это в действии?
00:06:17Вот демо магической ссылки. Я просто введу свой email.
00:06:24Наш воркфлоу запустился и отправил письмо. И вебхук просто ждет.
00:06:31На самом деле наш воркфлоу сейчас приостановлен. То есть потребляется ноль ресурсов, пока мы ждем клика по ссылке.
00:06:41О, а как это выглядит в Vercel? Можно посмотреть на запущенный процесс?
00:06:47Хорошо, письмо пришло. Но прежде чем я кликну, давайте взглянем на мониторинг.
00:06:52Я знаю, что перескакиваю, но мне нравится, что мы на это смотрим.
00:06:57Итак, мы видим, что наш запуск здесь, он начался 40 секунд назад.
00:07:02Если мы заглянем внутрь, увидим стандартные функции мониторинга воркфлоу.
00:07:08Мы видим входные данные: мой email, который я ввел в форму.
00:07:13И что интересно, мы видим, что наш хук просто находится в ожидании.
00:07:17Ты сказал, что сейчас ничего не исполняется. Это просто мониторинг, но никто реально не сидит и не ждет моего клика.
00:07:25Именно. Хук ждет, sleep спит, и ни то, ни другое не потребляет вычислительных мощностей.
00:07:39Но мы видим наш хук, и если помните, они оба участвуют в promise.race.
00:07:46Так что один из них должен завершиться первым, чтобы воркфлоу продолжился.
00:07:50Нажимаю на ссылку... и мы видим редирект на страницу успеха, как и было прописано в логике.
00:07:59И если я вернусь к форме входа...
00:08:01Верно, а в панели управления процесс тоже должен быть завершен.
00:08:05Точно. Наш воркфлоу завершился.
00:08:08И видно, что таймер тоже остановился, как только хук «выиграл».
00:08:11Да, мы внедрили магическую ссылку примерно в 50 строках кода.
00:08:17Это очень изящно. Здорово видеть, как схема работы магической ссылки на доске...
00:08:27совпадает с тем, как это выглядит в коде — шаги в коде в точности повторяют логику.
00:08:34Никакой промежуточной базы данных, никаких лишних API-роутов. Код читается очень легко.
00:08:41Я думаю, это самый мощный аспект Workflow SDK — возможность структурировать логику приложения естественно, а не подстраивать её под инфраструктуру.
00:08:59Верно. И мне нравится идея с вебхуками здесь — само название дает новый взгляд на них.
00:09:07Это просто эфемерный URL, который можно создать и на котором можно приостановиться.
00:09:10Это хороший переход, потому что я думаю о том, что мы в Vercel создаем много агентов.
00:09:16Мы делаем агентов для Slack и GitHub, и часто подписываемся на их вебхуки, верно?
00:09:23Каждый раз, когда появляется новый комментарий в PR, мы хотим запустить агента Vercel на основе события от GitHub.
00:09:31Можно ли использовать вебхуки Workflow, чтобы подписываться на события от GitHub, например?
00:09:36Для вебхуков от Slack или GitHub обычно нужно зайти в панель управления и вручную настроить статический URL обратного вызова.
00:09:49Верно. Там нельзя создавать одноразовые URL, как мы сделали с email.
00:09:54Верно. Функция createWebhook более высокого уровня, она генерирует уникальный случайный URL,
00:10:04который привязан к одному конкретному запуску воркфлоу.
00:10:07А роут для GitHub или Slack может быть связан с любым количеством запусков.
00:10:14Точно. Нужно заранее сконфигурировать что-то одно, хотя пул-реквестов много, все они идут на один эндпоинт.
00:10:20Чтобы реализовать это с помощью Workflow SDK, мы спустимся на уровень ниже и используем примитив hook.
00:10:28У меня как раз есть демо, чтобы показать это.
00:10:31Давайте посмотрим.
00:10:32Окей. Это бот «Время историй» (storytime bot).
00:10:35Это самое первое приложение, которое я написал на Workflow SDK чуть больше года назад.
00:10:40Работает оно так: вы вводите команду /storytime, и создается новый тред.
00:10:47Каждый тред представлен отдельным запуском воркфлоу.
00:10:52Когда мы открываем тред, мы видим, что ИИ начал историю, а вы, я или кто угодно в канале можем её продолжить.
00:11:05И ИИ поможет нам довести её до финала.
00:11:09Окей. У Луны есть магическое семя, что дальше? Она его сажает.
00:11:13Мы видим, что работа пошла.
00:11:17Что дальше? Что-то магическое.
00:11:20Наша история закончена, вот финал, и сейчас еще сгенерируется картинка.
00:11:26Но мы к этому вернемся.
00:11:28Мне уже очень любопытно, я ожидал один запрос к вебхуку, но их было как минимум два, ведь было два сообщения.
00:11:35Очень интересно посмотреть на код.
00:11:38Хорошо. Это функция воркфлоу для нашего бота.
00:11:44Она принимает ID канала — канала нашего бота.
00:11:50В неё можно передать некоторые настройки.
00:11:53Но интересно то, что здесь есть массив messages. Если вы знакомы с AI SDK, это формат данных для хранения переписки с ИИ.
00:12:04В типичном боте для Slack вам пришлось бы хранить это в базе данных и при каждом событии вебхука (новом сообщении) восстанавливать состояние и искать переписку в базе.
00:12:23Но здесь происходит не это. Это просто массив внутри вашей функции.
00:12:27Да. Я усмехнулся вначале, потому что увидел комментарий в коде: «Смотри, мам, никаких очередей или баз данных».
00:12:34И здесь нет импорта базы данных. Вы импортируете только Workflow.
00:12:40И возвращаясь к сообщениям: это легко пропустить, но у вас просто есть переменная final story, куда, как я понимаю, добавляются сообщения,
00:12:55и в итоге получается строка. Но база данных не нужна. Похоже, что «let» здесь и есть ваша база данных.
00:13:04Да, «let — это ваша база данных» — отличное выражение, надо его закрепить.
00:13:10Возможно, я его у тебя и украл, но...
00:13:14Интересный момент, ради которого мы здесь: функция hook. Мы создаем хук, но в отличие от примера с магической ссылкой, здесь мы передаем токен.
00:13:28Это строка, содержащая идентификатор, уникальный для данного запуска воркфлоу.
00:13:35TS — это ID треда. Так что эта строка — токен, который однозначно определяет этот запуск.
00:13:44Когда мы посмотрим на код роута вебхука, мы увидим, что данные от Slack содержат всё необходимое, чтобы детерминированно воссоздать этот ID.
00:13:58В этом и заключается магия того, как вебхук сопоставляется с конкретным запуском воркфлоу.
00:14:04Да, мне было интересно, ведь для магической ссылки создавались новые URL, а в Slack-боте нельзя давать новый URL для каждого треда.
00:14:17То есть вы получаете сообщение и вычисляете тот же токен на стороне возобновления.
00:14:29Воркфлоу может ждать этот токен, а вы конструируете его из данных сообщения, чтобы продолжить выполнение.
00:14:37Именно. Slack-бот был настроен один раз через панель управления Slack, где нужно указать статический URL.
00:14:50Поэтому низкоуровневый примитив hook здесь подходит лучше — мы можем динамически воссоздать токен.
00:14:59Давайте быстро взглянем на роут вебхука, там на самом деле не так много кода.
00:15:07Главное — как мы воссоздаем токен из данных, присланных Slack.
00:15:13Затем мы вызываем функцию resume, и это возобновляет именно тот самый запуск воркфлоу.
00:15:20Это очень круто. Я так понимаю, с вебхуками вы делаете примерно то же самое.
00:15:28Вебхук — это просто случайный токен и эндпоинт, который его разрешает?
00:15:35Да, разница лишь в том, что для функции вебхука вам не нужно прописывать API-роут в своем коде.
00:15:44Workflow SDK сам реализует дефолтный роут для этой функции.
00:15:50Но в остальном — это сгенерированный токен, уникальный для одного запуска.
00:15:55Но в данном случае у нас есть хук с токеном, и этот хук может получать данные несколько раз.
00:16:06Это отличается от примера с магической ссылкой, где срабатывание было однократным.
00:16:11Здесь мы хотим, чтобы хук срабатывал на каждое новое сообщение в треде Slack.
00:16:17Для этого используется синтаксис for await в JavaScript, обычный для асинхронных итераторов.
00:16:25В данном случае мы получаем несколько порций данных от вебхука Slack через наш хук.
00:16:33Это так круто. Я никогда не находил хорошего применения... Я люблю асинхронные итераторы и генераторы, когда-то даже делал доклад об этом.
00:16:42Но они всегда были хороши только для демо, я не знал, как их реально использовать.
00:16:46А здесь это выглядит просто как цикл.
00:16:50Но вместо перебора фиксированного набора элементов, вы используете for await на хуке — и цикл идеально ложится на задачу.
00:17:01Всё внутри цикла соответствует одному сообщению пользователя.
00:17:05Это отличный способ мышления: новое сообщение вызывает новую итерацию цикла, всё встает в очередь и продолжается.
00:17:12Прелесть в том, что на каждой итерации, пока мы ждем нового сообщения, вычислительные ресурсы вообще не тратятся.
00:17:22Воркфлоу полностью приостановлен, и следующее сообщение может прийти через минуты, дни или никогда — это не проблема.
00:17:33То есть в том канале могут быть треды, где запуск просто висит неделями в ожидании ответа?
00:17:42Это очень круто.
00:17:43И возвращаясь к массиву messages: теперь мы просто модифицируем массив.
00:17:48Добавляем новое сообщение — и это и есть наше изменение «базы данных», потому что массив — локальная переменная.
00:17:57Обалденно. И я вижу, вы используете promise.all для параллельного выполнения шагов.
00:18:03Код выглядит очень чисто для каждого сообщения в Slack.
00:18:08Мне нравится, что именно так я бы моделировал задачу на каком-нибудь хакатоне.
00:18:12Просто записать, что должно происходить при каждом сообщении.
00:18:16Да, модель с promise.all — это обычные функции useStep, которые мы запускаем параллельно.
00:18:23Например, добавление реакции на сообщение в Slack нужно для быстрой обратной связи пользователю.
00:18:32И одновременно мы запускаем ИИ, чтобы он продолжал генерировать историю.
00:18:39Мне было бы очень интересно посмотреть мониторинг, когда будет возможность. Представляю, как эти процессы запускаются одновременно.
00:18:49У нас есть мониторинг для «Времени историй».
00:18:52Он завершен, так что нужно будет проверить ту картинку.
00:18:56Вот наш хук.
00:18:58И здесь интересно то, что у нас два события получения данных хуком.
00:19:05Они соответствуют двум сообщениям, которые я отправил в тред Slack.
00:19:10Мониторинг позволяет увидеть конкретные данные, которые были переданы в хук.
00:19:14О, это очень удобно.
00:19:16То есть это данные от Slack. И вам не нужно было их отдельно логировать, они просто отображаются как события в панели управления.
00:19:25Верно. И каждый раз при получении данных выполнение воркфлоу продолжается, и шаги идут дальше.
00:19:34И в конце мы получаем результат генерации картинки для истории.
00:19:40Вот так работает бот «Время историй».
00:19:42Действительно здорово.
00:19:43Здорово увидеть вебхуки и в магических ссылках, и использование низкоуровневых хуков в цикле для обработки множества событий.
00:19:54Это очень круто.
00:19:55Кажется, эта модель идеально подходит для взаимодействия человека с системой через вебхуки.
00:20:02А для чего еще можно использовать хуки?
00:20:05О, определенно есть еще варианты.
00:20:06Последнее демо, которое я подготовил, использует похожий паттерн.
00:20:17Но здесь мы используем вебхук, чтобы передать выполнение кода куда-то еще и ждать завершения вычислений на другой стороне.
00:20:32Пока воркфлоу приостановлен, сторонний сервис вызывает наш вебхук, и мы завершаем логику приложения.
00:20:41В этом примере мы возьмем Vercel Sandbox для длительной операции, например, конвертации файла через FFmpeg.
00:20:51Итак, вот наш воркфлоу конвертации через FFmpeg.
00:20:56Первым делом мы создаем Vercel Sandbox.
00:21:00Интересно, что NPM-пакет Sandbox предоставляет функции, которые внутри себя уже используют useStep.
00:21:09Таким образом, эта операция фактически является Шагом (Step).
00:21:12То есть вы можете выпустить свой NPM-пакет.
00:21:15Sandbox по сути просто поставляется как пакет с директивой use Step внутри этой функции.
00:21:21Поэтому, когда вы импортируете и используете его в рабочем процессе, он автоматически делает Sandbox Шагом.
00:21:29Но это не значит, что вы не можете использовать Sandbox для создания чего-то вне рабочего процесса.
00:21:32Что произойдет, если вызвать это без рабочего процесса?
00:21:35Если вы понимаете, что директива — это просто строка, то при выполнении без компилятора рабочих процессов...
00:21:47эта строка ничего не делает. Так что это просто работает.
00:21:49Добавление use Step в ваши NPM-пакеты отлично работает и без Workflow SDK.
00:21:55А как только вы используете эту функцию внутри Workflow SDK, вы сразу получаете преимущества отказоустойчивости.
00:22:03Итак, Sandbox просто выполняет типичные вещи.
00:22:07Он устанавливает FFmpeg, так как по умолчанию он недоступен.
00:22:11Затем загружает файл по указанному нами URL.
00:22:14И каждый из этих запусков сейчас тоже является отдельным Шагом?
00:22:17Да, они запускают отдельные команды в Sandbox, и это Шаги. Мы увидим их в панели мониторинга.
00:22:29И затем мы возвращаемся к вызову create-webhook, который вы можете помнить по демо с магической ссылкой.
00:22:36Но в данном случае мы просто передадим этот URL вебхука в наш Bash-скрипт, который запустим в Sandbox.
00:22:43Что здесь происходит: мы запустим FFmpeg и сконвертируем файл в формат, запрошенный в интерфейсе.
00:22:53И когда всё будет готово, Bash-скрипт выполнит cURL-запрос к нашему URL обратного вызова из вебхука.
00:22:59И как только этот cURL-запрос поступит, логика нашего рабочего процесса возобновится.
00:23:04Понятно. Это круто. Я заглянул немного вперед и заметил оператор AND в этом запуске.
00:23:11То есть вы запускаете скрипт в фоновом режиме, потому что такой Шаг FFmpeg может занять много времени.
00:23:17Вам не нужно, чтобы Шаг просто висел и ждал завершения.
00:23:20Верно. Эта строка прямо здесь запускает наш скрипт конвертации FFmpeg в фоновом режиме.
00:23:28Затем функция рабочего процесса приостанавливается, и мы ждем возобновления через вебхук.
00:23:34И я снова вижу Promise.race с часовым ожиданием. Это отличный паттерн.
00:23:40Да, на этот раз процесс конвертации FFmpeg может занять длительное время.
00:23:46Это может быть очень большой медиафайл, поэтому в данном случае мы устанавливаем таймаут в один час.
00:23:51И это абсолютно нормально. В рабочем процессе можно ожидать практически неограниченное время.
00:23:56И опять же, пока мы ждем возобновления вебхука, вычислительные ресурсы вообще не потребляются.
00:24:01Мы можем это увидеть? Можем посмотреть, как это работает? У нас есть демо?
00:24:04Да.
00:24:05Это немного забавный пример.
00:24:07О, я сразу узнал кролика Big Buck Bunny. Это из Blender.
00:24:12Да, я помню, как смотрел эти видео, когда давным-давно изучал Blender.
00:24:16Ого, я тебе завидую.
00:24:19Итак, мы вставили URL медиафайла. В данном случае мы просто извлечем из него аудиодорожку.
00:24:26Как только мы нажимаем кнопку, запускается рабочий процесс, и мы можем перейти в панель мониторинга.
00:24:33А вот и он. Мы видим создание нашего Sandbox.
00:24:37И он возвращает экземпляр Sandbox. Довольно круто.
00:24:42Это потому, что в рабочем процессе всё должно быть сериализуемым.
00:24:46Но, как вы сказали, Sandbox поддерживает сериализацию, поэтому они отображаются в рабочем процессе.
00:24:53Верно. Пакет Vercel Sandbox содержит класс Sandbox, в котором реализованы функции сериализации рабочего процесса.
00:25:03Поэтому всё отлично работает в нашей панели мониторинга.
00:25:06То есть это может любой пакет, верно? Любой класс может реализовать те же символы и директивы use Step.
00:25:17Да, именно так. Мы видим, что наш хук был вызван обратно через 20 секунд.
00:25:25На этот раз конвертация прошла быстрее, так как файл небольшой, но это могло занять любое время.
00:25:31Видно, что после создания и инициализации Sandbox был создан хук, который мы передали для запуска FFmpeg.
00:25:43И когда работа завершилась, мы получили ответ (payload) от нашего Sandbox.
00:25:48Это тот самый cURL из bash-скрипта. Он выполняет команду, а затем через cURL завершает вебхук.
00:25:57Верно. Sandbox закончил работу и передает управление обратно нашему рабочему процессу.
00:26:04Теперь я думаю об этом так: вы запускаете Шаг, он выполняет код в фоне и продолжает процесс.
00:26:13Но Hook и Webhook кажутся чем-то более низкоуровневым. Можно просто создать токен или URL и ждать чего угодно.
00:26:21Это может быть магическая ссылка для человека, email, Sandbox или любое другое вычисление.
00:26:27Рабочий процесс просто замирает со всем своим состоянием до наступления события. Это как бы глубже, чем Step.
00:26:34Да. Я рассматриваю Webhook и Hook как способ передачи внешних данных в ваш рабочий процесс.
00:26:42Для меня Step — это способ приостановить процесс, дождаться завершения вычислений и возобновить его.
00:26:50Но Hook и Webhook действительно кажутся более базовыми, потому что вы создаете токен или URL для отправки куда угодно.
00:27:01Это может быть человек, электронная почта или, например, другой рабочий процесс.
00:27:05И когда это завершается, родительский рабочий процесс просыпается и продолжает ровно с того же места.
00:27:12Так что это в каком-то смысле уровень ниже, чем Step. Способ приостановки для любого внешнего действия.
00:27:19Да. Мне нравится думать о Hook как о способе приостановки процесса в ожидании внешних данных.
00:27:31Это очень круто. Наше время вышло, но эти демо снова подтвердили, почему Hook — моя любимая функция.
00:27:42Здорово. Рад, что вам понравилось.

Key Takeaway

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

Highlights

  • Реализация механизма магических ссылок через Workflow SDK сокращает объем кода до 50 строк и устраняет необходимость в Redis.

  • Примитив hook позволяет приостанавливать выполнение процесса без потребления вычислительных мощностей до момента получения внешнего события.

  • Использование асинхронных итераторов for await на хуках позволяет обрабатывать неограниченное количество сообщений в одном запуске воркфлоу.

  • Метод createWebhook с опцией respondWithManual позволяет функции воркфлоу напрямую отправлять HTTP-ответы, например редиректы.

  • Интеграция директивы useStep в сторонние NPM-пакеты обеспечивает их автоматическую отказоустойчивость внутри инфраструктуры воркфлоу.

  • Функция sleep заменяет механизмы TTL в базах данных для реализации таймаутов через стандартный паттерн promise.race.

Timeline

Проблемы традиционной реализации магических ссылок

  • Традиционный подход требует минимум три эндпоинта и внешнюю базу данных для хранения состояния сессии.
  • Необходимость настройки TTL в Redis и очистки устаревших данных усложняет архитектуру проекта.
  • Логика приложения оказывается распределена по множеству файлов, что создает хаотичный код.

Классическая система входа по магической ссылке опирается на Redis для отслеживания состояния между моментом запроса ссылки и моментом клика. Это вынуждает разработчиков внедрять механизмы удаления данных по истечении времени и обрабатывать потенциальные сбои при отправке писем. В такой модели база данных становится единственным способом синхронизации независимых HTTP-запросов.

Упрощение логики входа через Workflow SDK

  • Функция createWebhook создает уникальный URL, привязанный к конкретному экземпляру выполнения.
  • Паттерн promise.race в сочетании со sleep моделирует таймаут ожидания без использования внешних таймеров.
  • Состояние процесса сохраняется автоматически без настройки баз данных или крон-джобов.

При использовании воркфлоу весь процесс входа описывается в одном файле линейным кодом. Использование useStep гарантирует повторную отправку письма в случае ошибки, а функция воркфлоу полностью замирает, ожидая клика пользователя по ссылке. Это исключает затраты ресурсов на ожидание и позволяет избежать использования Redis для кратковременного хранения email-адреса и токена.

Обработка потоковых событий в чат-ботах

  • Примитив hook с детерминированным токеном сопоставляет входящие данные от Slack с конкретным запуском.
  • Локальная переменная let внутри функции воркфлоу заменяет базу данных для хранения истории переписки.
  • Цикл for await обеспечивает обработку каждого нового сообщения в треде как новой итерации.

В примере с ботом «Время историй» (storytime bot) каждое сообщение в Slack инициирует возобновление воркфлоу через функцию resume. Поскольку состояние функции сохраняется, массив сообщений остается доступным между итерациями без необходимости загрузки из БД. Это позволяет строить сложные диалоговые системы, где контекст хранится в памяти функции на протяжении дней или недель.

Интеграция с внешними вычислительными средами

  • Вебхуки позволяют передавать управление сторонним сервисам, таким как Vercel Sandbox, для выполнения тяжелых задач.
  • Фоновое выполнение bash-скриптов с последующим cURL-уведомлением возобновляет приостановленный воркфлоу.
  • Сериализуемые классы в NPM-пакетах позволяют отображать сложные объекты в панели мониторинга воркфлоу.

Для длительных операций, таких как конвертация видео через FFmpeg, воркфлоу запускает Sandbox и создает URL обратного вызова. После завершения конвертации скрипт отправляет сигнал через вебхук, и воркфлоу продолжает выполнение с полученным результатом. Этот метод позволяет эффективно распределять нагрузку, не блокируя основной поток выполнения приложения.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video