Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Специфические утечки памяти + предложение по улучшению функционала AsyncResult (результатов асинхронной операции) #1022

Open
JohnSergeev opened this issue Jun 14, 2024 · 0 comments
Labels
enhancement Предложение по улучшению или добавлению функциональности

Comments

@JohnSergeev
Copy link

Добрый день.

Общая информация для упрощения «разбора полётов»:
Организация: «ДНС Ритейл»
ИНН: 2540167061
КПП: 254301001
ID организации: [email protected]
ID участника ЭДО: 2BM-2540167061-254001001-201312151140099321090

Мы работаем с Диадок с использованием компонент АПИ для 1С
https://diadocsdk-1c.readthedocs.io/ru/latest/index.html
Текущая используемая версия: COM x64 5.51.3.965.

Недавно столкнулись с проблемой, когда на сервере ЭДО у нас внезапно стала крайне быстро выжираться вся память. Запущенный rphost примерно за 8 минут выжирал 16 Гб памяти сервера, после чего перезапускался – и всё начиналось сначала.

Разбор проблемы показал очень специфическую причину, из-за которой возникала эта проблема.

Суть проблемы

У нас есть фоновый процесс 1С, обновляющий информацию о статусах взаимодействия наших контрагентов с юр. лицами нашей компании. Для получения информации по контрагентам, мы используем асинхронные методы GetCounteragentListByStatusAsync(). Мы запускаем по всем организациям (+ в разрезе отдельных статусов) набор асинхронок, получаем в ответ AsyncResult, складываем их в массив и передаем на дальнейшую обработку. Там мы ожидаем завершения асинхронки (т.е. ждем, когда AsyncResult.IsCompleted = Истина) и обрабатываем полученные в AsyncResult.Result данные.

Утечки памяти начались тогда, когда при обработке вызовов GetCounteragentListByStatusAsync() «где-то посредине» серии запусков, мы стали стабильно получать ошибку при вызове этого метода. Это было связано с чисто административными причинами, не в них суть. Просто благодаря им мы «влетели» в такую ситуацию.

Т.к. вызов метода АПИ завершался ошибкой, то наш код фиксировал ошибку в специальном журнале и завершал работу. НО! Само обновление информации по контрагентам не останавливалось. В том смысле, что через какое-то время фоновый процесс снова запускал тот же самый код и падал в том же самом месте. Само по себе – совершенно нормальное поведение – есть код, в нем есть контроль и обработка ошибок. Чего ещё надо?

Но так получилось, что у основного нашего юр. лица (организации в ЭДО) слишком много контрагентов. И когда мы вызываем для неё метод GetCounteragentListByStatusAsync(«IsMyCounteragent»), то в ответ получаем список контрагентов ЭДО размером более 260.000 (двухсот шестидесяти тысяч!) контрагентов.

Что происходит:

  1. Мы запускаем GetCounteragentListByStatusAsync(), получаем AsyncResult и кладем в массив.
  2. Продолжаем выполнять в цикле п.1 и получаем массив AsyncResult.
  3. При выполнении цикла уже созданные асинхронки уже работают.
  4. Где-то (еще перед ошибкой с ЭП!) в цикле мы запускаем асинхронки по нашей основной организации (в которой только на «IsMyCounteragent» в возврате ~260.000 контрагентов).
  5. После, где-то в середине цикла мы нарываемся на ошибку и завершаем цикл и всё выполнение обновления.
    !!!!!!!
  6. Через какое-то время запускаем весь процесс с п.1 заново.

И между пунктами 5 и 6 происходит следующее:

Несмотря на то, что мы завершили код 1С и массив, в котором хранятся полученные AsyncResult, очищается – выполнение кода в запущенных асинхронных потоках явно продолжается!

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

Т.е. если мы с помощью GetCounteragentListByStatusAsync() или какой-то другой асинхронной операции, запустили какой-то фоновый процесс, то даже, если переменные, в которые мы получили AsyncResult очистить и перестать выполнять код 1С, вызвавший GetCounteragentListByStatusAsync(), получается, что запущенный фоновый процесс всё равно продолжает работать до тех пор, пока не закончит делать то, что «ему сказали».

Что делать?

Мы «полечили» выявленную проблему следующим образом – теперь, если код получения массива асинхронок или обработки уже полученных асинхронок падает по ошибке, то перед тем, как зафиксировать ошибку в итерации обновления и перейти к новой итерации – мы просто запускаем цикл ожидания.

И ждем, пока все асинхронки, из имеющихся в массиве асинхронок, не закончат свою работу и не вернут AsyncResult.IsCompleted = Истина.

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

Что хотелось бы

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

Принудительно завершать ранее запущенные асинхронки, не дожидаясь завершения их выполнения.

Т.е. хотелось бы, чтобы у объектов AsyncResult кроме свойств IsCompleted и Result:
изображение
Появился бы какой-то метод по досрочному прерыванию асинхронного процесса и освобождению памяти.
Что-то типа «AsyncResult.Terminate()».

Тогда в подобных ситуациях пользователи АПИ, понимая, что они что-то асинхронное позапускали, а ждать смысла уже нет (в силу возникновения каких-то проблем и т.п.) – могли бы явно завершить выполнение запущенных асинхронных процессов и освободить память, не дожидаясь, пока эти процессы завершаться естественным образом.

Спасибо.

@GilimkhanovDenis GilimkhanovDenis added the enhancement Предложение по улучшению или добавлению функциональности label Jun 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Предложение по улучшению или добавлению функциональности
Projects
None yet
Development

No branches or pull requests

2 participants