Запретить запуск заданий в кластере для рабочего кода во время развертывания

У меня есть сценарий, который выполняется в течение нескольких минут как задание в кластере в производственной среде. В кластере одновременно выполняется от 0 до 100 таких заданий, каждое из которых содержит по одному сценарию на задание. Обычно таких заданий нет или их бывает всплеск из 4-8.

Я хочу предотвратить запуск таких заданий при развертывании новой версии кода в рабочей среде.

Как мне это сделать, чтобы оптимизировать ремонтопригодность?

Моя первоначальная идея была такая:

  1. Используйте файл семафора или файл блокировки, который создается в начале развертывания, а затем удаляется после развертывания кода. Развертывание занимает 0,5–10 минут в зависимости от сложности текущих задач развертывания.
  2. Этот файл блокировки также автоматически удаляется отдельным заданием cron, например, через 30 минут, если при развертывании не удается удалить этот файл. Например, если деплой грубо прервали, этот файл не должен вечно болтаться, блокируя задания. То есть файл удаляется отдельным заданием cron, если он старше 30 минут.
  3. Производственный код проверяет наличие этого файла блокировки и ждет, пока он не исчезнет. Так что задания ждут не более 30 мин.

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

Возможно, в Capistrano есть стандартный механизм достижения этой цели, который используется для развертывания этого кода?

Примечания:

  • Когда вы ответите на вопрос, сравните ремонтопригодность предложенного вами решения с простым решением, которое я предлагаю выше (с использованием файлов блокировки).

  • Я не уверен, нужно ли мне учитывать условия гонки. То есть действительно ли эта система (с файлами блокировки) подвержена гонкам? Или это маловероятно?

Часто задаваемые вопросы:

Есть ли особая причина, по которой эти задания не следует запускать во время развертывания?

У меня были случаи, когда несколько заданий запускались в середине развертывания и из-за этого завершались сбоем. Поиск и повторный запуск таких неудачных заданий отнимает много времени. Задержка их во время развертывания приводит лишь к небольшому и редкому снижению производительности и является, безусловно, наиболее приемлемым решением. Для нашей системы ремонтопригодность является приоритетом номер один.

Re: Файл блокировки. Имейте в виду, что cron может запускаться каждые 30 минут, но вы можете начать развертывание на минуте (например) 29. Ваш файл блокировки будет существовать только минуту, прежде чем cron сотрет его, и вы вернетесь к исходному состоянию. проблема.

Jacob Miller 17.04.2024 20:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
1
640
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Псевдокод вашей вакансии может выглядеть так (пока я не знаю вашего кода вакансии)

class ProcessingJob < ApplicationJob
  queue_as :default

  def perform
    return unless Flipper.enabled?(:jobs_processing)

    ... job's code
  end
end

Flipper имеет пользовательский интерфейс администратора для включения/отключения флажков функций. Так, например, вы можете создать функцию jobs_processing, включить ее, а затем в какой-то момент перед развертыванием отключить ее.

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

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

class CreateFeatureFlags < ActiveRecord::Migration[7.1]
  def change
    create_table :feature_flags do |t|
      t.string :name, null: false, index: { unique: true }
      t.timestamps
    end
  end
end

class FeatureFlag < ApplicationRecord
  def self.enabled?(name)
    where(name: name).exists?
  end
end

FeatureFlag.enabled?('jobs_processing')

Спасибо большое за интересное, изящное и полезное решение! Мне это нравится больше, чем решение, которое я предложил в своем вопросе (блокировка на основе файлов).

Timur Shtatland 11.04.2024 20:42

У меня был один вопрос, который важен для нашей группы. Как говорится в моем вопросе: «Для нашей системы ремонтопригодность является приоритетом номер один». Не могли бы вы сравнить ремонтопригодность предлагаемого вами решения с простым решением, которое я предложил в вопросе (с использованием файлов блокировки). Неспециалисту может показаться, что решение на основе файла блокировки проще в обслуживании. Решение на основе файлов блокировки не требует внешних (потенциально, а позже и неподдерживаемых) драгоценных камней, базы данных или дополнительных таблиц. Решением на основе файлов блокировки также можно манипулировать в файловой системе без необходимости доступа к базе данных.

Timur Shtatland 11.04.2024 20:45

Другой вопрос, нужно ли учитывать условия гонки? То есть действительно ли эта система (с файлами блокировки) подвержена гонкам? Или это маловероятно? В последнем случае, возможно, более подходящим будет более простое решение на основе файлов блокировки. Обратите внимание: хотя я и пытаюсь критически оценить оба решения, лично я, как уже упоминал, предпочитаю ваше решение. Еще раз спасибо!

Timur Shtatland 11.04.2024 20:54

Условие гонки не имеет значения — в обоих случаях (установка флага функции или какой-либо файл блокировки) при отключении заданий вы просто ждете, пока завершатся уже запущенные задания, и запускаете развертывание.

Anton Bogdanov 12.04.2024 09:26

О файле блокировки. Представьте, что вам нужно будет предоставить не-разработчику возможность включать/отключать флаги функций (возможно, в будущем будет больше флагов), что вы выберете — установить флаг в простом пользовательском интерфейсе или предоставить доступ к серверу?

Anton Bogdanov 12.04.2024 09:28

Спасибо! Предоставление доступа к этому лицу, не являющемуся разработчиком, является хорошим и убедительным аргументом против решения с использованием файла блокировки.

Timur Shtatland 12.04.2024 19:50
Ответ принят как подходящий

Работа с консультативными замками на самом простом уровне с помощью psql.

Сессия 1

select pg_advisory_lock(3752667);

Содержимое файла Advisory_lock_test.sql:

select pg_advisory_lock(3752667);
select "VendorID" from nyc_taxi_pl limit 10;

Затем сеанс 2:

psql -d test -U postgres -p 5452 -f advisory_lock_text.sql 
Null display is "NULL".

Затем в сеансе 1:

select pg_advisory_unlock(3752667);

Вернемся к сеансу 2:

Null display is "NULL".
 pg_advisory_lock 
------------------
 
(1 row)

 VendorID 
----------
        1
        2
        2
        2
        2
        2
        1
        1
        2
        2
(10 rows)

Примечание:

Ниже используются блокировки уровня сеанса. Блокировки транзакций также доступны с помощью pg_advisory_xact_lock

По сути, вы создаете блокировку в сеансе с помощью pg_advisory_lock(3752667), где число может быть одним 64-битным целым числом из двух 32-битных целых чисел. Они могут быть получены из значений, которые вы извлекаете из таблицы, поэтому число привязано к определенному действию, например. select pg_advisory_lock((select lock_number from a_lock where action = 'deploy'));. Затем во втором или других сеансах вы пытаетесь заблокировать тот же номер. Если номер используется, не разблокирован или исходный сеанс не завершился, другие сеансы будут ждать, пока исходный сеанс не снимет блокировку. В этот момент будут выполнены остальные команды.

В вашем случае создайте номер, возможно, в таблице, связанный с развертыванием. При запуске развертывания заблокируйте номер перед выполнением изменений, а затем разблокируйте его в конце развертывания. Если развертывание завершится неудачей и сеанс завершится, блокировка также будет снята. Другие сценарии также должны будут начаться с попытки заблокировать этот номер. Если он используется, они подождут, пока он будет освобожден, а затем запустят остальные команды сценария и разблокируют его. Насколько это осуществимо, зависит от количества сценариев, с которыми вы имеете дело, и от того, насколько сильно люди будут придерживаться этого процесса.

Большое спасибо за ответ с подробными примерами! Как я могу снять блокировку, если она осталась позади из-за развертывания, которому не удалось снять блокировку? В случае с файлами блокировки я предложил сделать так: "Этот файл блокировки также автоматически удаляется отдельным заданием cron через, например, 30 мин, если при деплое не удается удалить этот файл. Например, если деплой в грубо убитом виде , этот файл не должен вечно блокировать задания. То есть файл удаляется отдельным заданием cron, если его возраст превышает 30 минут».

Timur Shtatland 16.04.2024 21:03

1) Если развертывание не удалось и сеанс завершился, блокировка будет снята. 2) Вы все равно можете использовать задание cron в качестве резервной копии. В нем сделайте select pg_advisory_unlock(3752667);, который выдаст предупреждение, если блокировка уже снята. Если вы хотите выглядеть так, прежде чем прыгнуть, вы можете выбрать один из pg_locks: select * from pg_locks where locktype = 'advisory' and objid = 3752667;

Adrian Klaver 16.04.2024 21:16

Вы также упомянули блокировки транзакций. Когда мне следует использовать блокировки транзакций, а именно? ситуация, которую я описываю (развертывание)? Я нашел это, но никаких дальнейших указаний: «Блокировки могут быть установлены на уровне сеанса (чтобы они удерживались до тех пор, пока они не будут освобождены или пока не завершится сеанс) или на уровне транзакции (чтобы они удерживались до тех пор, пока не завершится текущая транзакция; никаких условий не существует». для выпуска вручную).", PostgreSQL: Документация: 16: 9.27. Функции системного администрирования

Timur Shtatland 16.04.2024 21:28

1) Запрос на блокировку должен быть select * from pg_locks where locktype = 'advisory' and objid = 3752667 and database = (select oid from pg_database where datname = 'test');, поскольку рекомендательные блокировки предусмотрены для каждой базы данных. 2) Прочтите это Консультативные блокировки, где более подробно описаны блокировки транзакций и сеансов. Блокировки транзакций автоматически снимаются в конце транзакции. Я предполагаю, что вам понадобятся блокировки сеансов.

Adrian Klaver 16.04.2024 22:12

«Задание cron в качестве резервной копии. В нем выполните select pg_advisory_unlock(3752667);», вы не можете pg_advisory_unlock() заблокировать эту блокировку из cron. За пределами сеанса, в котором удерживается блокировка, он всегда возвращается false с предупреждением. Вы можете проверить, находится ли он в pg_locks, но чтобы освободить его из-за пределов сеанса владельца, вам придется запустить pg_terminate_backend(), убивая весь сеанс, удерживающий блокировку. Если это уровень транзакции, вы можете pg_cancel_backend() настроить таргетинг на транзакцию. Или вместо этого установите таймауты

Zegarek 24.04.2024 15:01

Я расскажу о подходе к файлу флагов на основе ОС:

  1. Высокая ремонтопригодность и переносимость: вам просто нужен современный дистрибутив Unix, который поставляется со стандартом flock, а это значит, что большинство из них. Вы можете взаимодействовать с ним из оболочки, но он также доступен в большинстве современных языков программирования: Python fcntl.flock , Ruby File.flock , и т. д..

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

    • Если рабочий завершает работу/умирает, не удалив файл, он все равно снимает с него блокировку, поэтому его преемник все равно может захватить блокировку и не создавать новый файл.
    • Удаление файла флага не влияет на работника, у которого он открыт с блокировкой: он сохраняет блокировку и файл до тех пор, пока с ним не будет закончено, а также можно бесплатно запросить его удаление после этого, даже если было удалено уже находится на рассмотрении.
    • Если рабочий зависает, и вы удаляете его файл флага во время очистки, будущие рабочие могут свободно создать файл с тем же именем/идентификатором и заблокировать его, независимо от того, уничтожите ли вы зависшего рабочего, завершив удаление его файла флага. или позволить ему жить дальше, сохраняя устаревший образ файла.
  3. Очистка: используйте на в тандеме с cron. Как отметил @Джейкоб Миллер, если вы позволите cron обрабатывать удаление напрямую, его циклы могут не синхронизироваться с вашими развертываниями, что приведет к преждевременной очистке флагов. Если вместо этого вы скажете ему обнаруживать только файлы флагов для удаления и запланируете их удаление через 30 минут после обнаружения, вы гарантируете, что владелец флага сможет завершить свою работу в течение >=30 минут.

    Если вы часто меняете идентификаторы рекомендательной блокировки (имена файлов в этом сценарии), вы можете выполнить периодическую очистку, которая удалит самые старые разблокированные файлы, чтобы предотвратить беспорядок. Если вы повторно используете идентификаторы, любые оставшиеся файлы от прошлых исполнителей будут повторно использованы и в конечном итоге удалены будущими исполнителями.

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

@VonC Я собирался наградить ваш (теперь удаленный) ответ ниже +200 наградой, но, как я объяснил здесь, мне помешали это сделать. Очевидно, я разочарован. С тех пор я уже разместил следующую награду (+400), но она, скорее всего, уйдет в другой ответ (извините!). Тем не менее, мне все равно хотелось бы, чтобы ваш ответ каким-то образом был восстановлен/спасен, а затем за него проголосовали как я, так и остальная часть сообщества (это полезный ответ).

Timur Shtatland 02.05.2024 19:38

@VonC Не могли бы вы опубликовать отдельный ответ на основе удаленного, но без текста, вызвавшего удаление? Возможно, это можно как-то перефразировать? Я знаю, что, возможно, прошу многого, и все ради потенциальной награды в виде всего лишь одного положительного голоса (моего). Но ответ хороший, ИМХО. И я думаю, что другие проголосуют за это, возможно, не сразу. Заранее спасибо за ваше время! И, разумеется, я рад видеть, что ты вернулся. :)

Timur Shtatland 02.05.2024 19:44

@TimurShtatland Спасибо и, еще раз, извиняюсь. По понятной причине я не могу опубликовать незаконный ответ, даже в перефразированном виде. Но вы должны иметь возможность увидеть удаленное и опубликовать свое слово, основываясь на своих настройках и опыте, что-то, что поможет другим читателям.

VonC 02.05.2024 21:20

@VonC Я тоже думаю, что твой ответ стоило бы восстановить. Он продемонстрировал уникальный, индивидуальный подход, четкий и конкретный вариант использования, а также провел небольшое сравнение с другими ответами. Я думаю, что к тому времени, когда вы его удалили, человеческий вклад перевесил или даже вытеснил части, созданные ИИ.

Zegarek 02.05.2024 21:38

@Zegarek Спасибо за этот отзыв. Поймите, я не удалил ответ. Для меня оно было удалено модераторами, которые считают (справедливо), что любое количество частей, созданных ИИ, перевешивает все остальное.

VonC 02.05.2024 21:45

@VonC Спасибо за быстрый ответ! Я действительно вижу удаленный ответ. Ввиду отсутствия у меня знаний в этой теме, я воздержусь от публикации удаленного ответа своими словами. Это потому, что некоторые детали у меня над головой. Если это сделает кто-то другой (кто-то более квалифицированный), я, конечно, буду только приветствовать.

Timur Shtatland 02.05.2024 22:25

@VonC Что касается твоего недавнего поста: в моем случае извинений не требуется! Я благодарен вам как за оригинальный ответ, так и за размещение объяснений произошедшего на Meta SO. На мой взгляд, объяснение этого было правильным, и оно также показывает сообществу, как бороться с прошлыми ошибками и продуктивно двигаться вперед.

Timur Shtatland 02.05.2024 22:32

Другие вопросы по теме