LINUX.ORG.RU

Сохранить на диск/восстановить с диска процесс

 


0

1

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

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

Желательно, что бы для добавления такой функциональности приходилось минимально модифицировать код.

Сейчас это примерно так представляется, исходный код:

# всякая инициализация
...
t = 0
while t<=t_max:
    # итерации по времени
    ...
модифицированный код:
if not load_state():
   # всякая инициализация
   ...
   t = 0
while t<=t_max:
    # итерации по времени
    ...
    dump_state()
функция dump_state() дампит содержимое словаря __main__ (включая файлы - имя, режим доступа, позиция). Функция load_state() восстанавливает содержимое словаря __main__.

Но у такого подхода есть недостатки - исходный код может иметь гораздо более сложную структуру, ну и модифицировать его придется... ХЗ как в общем. В идеале бы хотелось задамтить не только данные, но и стек, и войти при потоврном запуске в то же самое место. Это как можно сделать малой кровью?

И как это вообще делают нормальные люди?;-)

★★★★★

Последнее исправление: AIv (всего исправлений: 1)

функция dump_state() дампит содержимое словаря __main__ (включая файлы - имя, режим доступа, позиция). Функция load_state() восстанавливает содержимое словаря __main__.

Скорее всего pickle и кастомные __reduce__ помогут.

В идеале бы хотелось задамтить не только данные, но и стек, и войти при потоврном запуске в то же самое место. Это как можно сделать малой кровью?

Скорее всего только какой-то такой ужас:

def stoppable(step=0):
    if step <= 0:
        print(0)
        yield

    if step <= 1:
        print(1)
        yield

    if step <= 2:
        print(2)
        yield

    if step <= 3:
        print(3)
holuiitipun
()
Ответ на: комментарий от gag

К сожалению на целевых машинах ни CRIU ни BLCR не стоят;-(

Я не спец. по администирированию, можно ли их развернуть локально из под юзера - че то сомнительно, да и они в общем избыточны.

Так то конечно им придется морозить все (если про CRIU говорить), но по сравнению с данными самого приложения это ерунда. Но все равно избыточно...

AIv ★★★★★
() автор топика
Ответ на: комментарий от holuiitipun

У меня была дурная мысль - если состояние сбрасывается из точки с верхнего уровня (с нулевым отступом), то можно при восстановлении, после формирования словаря __main__, просто выбросить исходники скрипта до места сброса а оcтальное ехеc-нуть;-)

AIv ★★★★★
() автор топика

есть ограничение по длительности работы приложения ... как это вообще делают нормальные люди?;-)

Выделяют в приложении отдельный объект-состояние и пишут для него сериализатор и десериализатор. Возьня с __main__ это идиотизм очень наивного человека и работать он, естестенно, не будет.

mashina ★★★★★
()

Ещё можно попробовать деать fork() через кванты времени - если повезёт, то система не перенесёт учётные данные на новый процесс.

mashina ★★★★★
()
Ответ на: комментарий от mashina

Почему в качестве этого объекта нельзя рассматривать сам __main__?

Ещё можно попробовать деать fork() через кванты времени - если повезёт, то система не перенесёт учётные данные на новый процесс.

ТОгда уже дабл форк. Но это читерство, и если не повезет то могут закрыть аккаунт. Машина то общая...

AIv ★★★★★
() автор топика
Последнее исправление: AIv (всего исправлений: 1)
Ответ на: комментарий от zolden

Спасибо, забавная штука. М.б. и оно...

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

Почему в качестве этого объекта нельзя рассматривать сам __main__?

Потому что не всё сериализуемо, в особенности это касается не питоновских библиотек и внешних объектов. Существуют так же проблемы с логикой - не любой код можно даже поставить на паузу на длительное время остановив его в произвольном месте. Потому в правильно спроектированном ПО явно отделяют сериализумое состояние и определяют точки останова. Такой код можно хотя бы обложить тестами и заниматься его отладкой в отличие от гогнокода который пытается сохранться обобщённо на уровне приложения.

ТОгда уже дабл форк.

Это смотря как лимиты устроены. Если они похожи на обычные ulimit, то достаточно завести новый процесс.

Но это читерство, и если не повезет то могут закрыть аккаунт.

А что ты хочешь сделать это не читерство? Эффект аналогичный за исключением занимаемой памяти между паузами.

mashina ★★★★★
()
Ответ на: комментарий от mashina

Потому что не всё сериализуемо, в особенности это касается не питоновских библиотек и внешних объектов.

Я в курсе, кэп. Но я таки умею сериализовывать все, что мне нужно. Понятно, что подход с сериализацией __main__ накладывает определенные ограничения на код, но это значительно удобней, чем мутить выделенный объект для сохранения состояния. В частности каждая реализация такого объекта для удже отлаженного кода будет неизбежно сопровождаться ошибками, которые на питоне будут вылезать через несколько часов (или дней) после запуска - это куда вероятнее чем поялвение чего то экзотического в __main__.

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

А что ты хочешь сделать это не читерство?

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

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

Я в курсе, кэп. Но я таки умею сериализовывать все, что мне нужно.

Отлично, но там же навряд ли только твои объекты, могут присутствовать системные через всякого рода ссылок.

... но это значительно удобней, чем мутить выделенный объект...

Это сильно зависит от структуры состояния. Если оно, например, достаточно объёмное и разбросано по коду как попало, то это уже трудно поддерживаемый гогнокод. Для наколеночных пятиминутных поделий, конечно, можно делать как получится.

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

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

mashina ★★★★★
()
Ответ на: комментарий от mashina

Отлично, но там же навряд ли только твои объекты, могут присутствовать системные через всякого рода ссылок.

В __main__? Всякая экзотика туда может попасть либо при import * для системного модуля (сам дурак, так не надо делать), либо если я ее создам. Я такое не создаю, я про такое не знаю;-) Если там что то такое и появится, это сразу будет видно на тестовой сериализации.

А нормальные люди сразу стараются отделять данные от логики.

Я тоже отделяю. Но в __main__ как раз данные и логика вынужденно взаимодействуют, но то он и __main__ ...

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

AIv ★★★★★
() автор топика
Ответ на: комментарий от AIv

Я тоже отделяю ... у меня нет проблем с сохранением данных - у меня есть проблема с сохранением точки вызова...

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

mashina ★★★★★
()
Ответ на: комментарий от mashina

Ну стек сохранить наверное можно - непонятно как его потом поднять. Подниматься то все будет в рамках одной версии (не думаю, что бы за каждый день на кластере питон меняли;-)), это же не на всю жизнь состояние сохраняется а только между квантами, потом оно не нужно...

Мне пока ничего в голову вообще, кроме как ставить точки на самом верхнем уровне, не приходит;-( Тогда как то можно разобраться, тупо на уровне исходников - точка порсото номер строки, поднимаем данные, собираем __main__.__dict__ (включая импорт модулей), дальше пускаем в работу кусок исходника начиная с точки.

AIv ★★★★★
() автор топика
Ответ на: комментарий от mashina

Можно конечно покурить сырцы pdb. Но это все же неск. другое, мне кажется без какой то аццкой магии поднять стек с диска и войти в нужное место невозможно;-(

AIv ★★★★★
() автор топика

Не будем спорить о golang vs python, а посмотрим на lua. На опеннете только что появилась новость, и я вспомнил, что недавно об этом как раз справшивали. About Arcan:

3. Built-in monitoring and crash-dump analysis. The engine can serialize vital internal state to a Lua script (“crash dump”) or to another version of itself periodically (“monitoring”) to allow external filters and tools to be written quickly in order to track down bugs or suspicious activity.

Врдуг, натолкнёт на идею.

gag ★★★★★
()
Ответ на: комментарий от gag

Спасибо, я подумаю.

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

AIv ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.