Первым делом смотрим виновника в мониторе ресурсов:
Видим что им является один из процессов rphost с id 8484. (Remote Process HOST)
Далее, смотрим все сеансы этого рабочего процесса, где-то в последних столбцах таблицы ищем потребление диска:
Видим 6 сеансов на этом процессе:
Ага, вот и наш ненасытный сеанс, открытый фоновым заданием под пользователем Adm в 9:57. В колонке "Запись (Текущая)" значение достигло 138 гб! (пред. скрин). Кстати, с этого же времени висит ещё одно. Может они связаны.
Далее идем в консоль заданий и ищем что его породило:
Смотрим код первого:
К сожалению по заданному пути никакого скрипта не было.
И оно, даже не смотря на блок попытки, просто зависало с ошибкой:
Ок, не наш случай. Добавим проверку на существование файла и идём дальше. Код второго регламентного:
Здесь уже интересней. При достаточно большом размере выбираемых данных до второй точки останова выполнение никогда не дойдет.
Когда в запросе выбирается реквизит типа хранилище значений с бинарными данными (поле ФайлДанных) — тогда в результирующую выборку попадает весь размер всех файлов одновременно!
В нашем случае это вызывает ошибку нехватки места на диске:
При выполнении метода Выполнить() данные запроса могут сбрасываться на диск, и вот почему:
http://its.1c.ru/db/v8std#content:-2145782922:1
1. Не следует разрабатывать решения исходя из неограниченного объема оперативной памяти. Для многопользовательских систем любое неэффективное использование памяти может катастрофически сказаться на работоспособности.
Следует избегать формирования больших структур данных в памяти. Если объём данных, с которыми работает бизнес-логика, сам по себе ничем не ограничен, его нужно ограничивать искусственно, обрабатывая данные порциями и сохраняя результаты в базу или файлы.
2. При потенциально неограниченных выборках данных из ИБ следует получать данные из базы порциями фиксированного размера.
Например, неправильно:Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка,
| Номенклатура.Наименование,
| Номенклатура.ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";// Выгрузка всего справочника в таблицу значений
Номенклатура = Запрос.Выполнить().Выгрузить();
Для каждого ПозицияНоменклатуры Из Номенклатура Цикл
// Обработка элемента справочника
// …
КонецЦикла;поскольку весь результат запроса сразу помещается в память, в таблицу значений.
Также неправильно:Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка,
| Номенклатура.Наименование,
| Номенклатура.ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";РезультатЗапроса = Запрос.Выполнить();
// Обход результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Обработка элемента выборки
// …
КонецЦикла;поскольку и в этом случае при выполнении запроса его результат будет сначала считан в память целиком (*).
* Примечание. Если размер результата запроса превосходит размер имеющейся памяти, то данные будут записаны на диск, а затем считаны оттуда в процессе вызовов Выборка.Следующий().
Правильно ограничивать результат запроса искусственно:
ВсеОбработано = Ложь;
Пока Истина Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка,
| Номенклатура.Наименование,
| Номенклатура.ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| <условие выборки необработанных записей>";РезультатЗапроса = Запрос.Выполнить();
ВсеОбработано = РезультатЗапроса.Пустой();
Если ВсеОбработано Тогда
Прервать;
КонецЕсли;// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Обработка элемента выборки
// …
КонецЦикла;КонецЦикла;
Также правильно:
Выборка = Справочники.Номенклатура.Выбрать(…, Отбор);
Пока Выборка.Следующий() Цикл
// Обработка элемента выборки
// …
КонецЦикла;поскольку в этом случае платформа 1С:Предприятие выполняет курсорный запрос.
Кроме того, число элементов выборки автоматически ограничивает платформа 1С:Предприятие в запросах динамических списков.
Таким образом, переписав код на получение значения хранилища через ключ записи, избавляемся от ошибки нехватки памяти:
Вывод: никогда не делайте неограниченную выборку полей типа ХранилищеЗначения, иначе забьёте память и диск!
Динамическая же выборка считывает по 25 записей за раз
upd: insurgut +
Полная выборка справочника (регистра) — интересно, а зачем?
(1) Вероятно, программист, написавший этот запрос предполагал, что данных в регистре будет немного и не учел того, что их размеры могут зависеть от выбора пользователей.
Всегда интересно почитать про расследования.
Я вот сейчас тоже веду два расследования. Пока безуспешно. Тех. журнал не помогает. Исключения, которые сыпятся в журнал зависят от данных в параметрах процедур, а ТЖ их не логирует никак, да еще и общие модули БСП. Пришлось ставить «ловушки» кодом, изменяя конфигурацию, посмотрю что именно попадается пользователю. Вторая — плавающая ошибка платформы, приводящая к краху всех клиентских сессий. Ошибка является следствием каких-то действий, но каких именно и кто был первым инициатором выяснить не удается. Исключение получается лавинообразным по всей системе.
Запрос.Выполнить().Выгрузить();
Запрос.Выполнить().Выбрать();
В одном случает улетает результат в оперативную память, в другом — на диске во временную папку.
Можете проверить и написать в каком из случаев куда летит результат запроса.
(2)
Меня смущает здесь полная выборка, а потом уже наложение условия внутри
Лучше соединить запрос этой функции с запросом с полной выборкой. Тогда сразу будет существенное сокращение результатов выборки
Это же всё ошибки доработок или разработчиков типовых поставок?)
По какому принципу определили, что именно этот сеанс виноват?
А разве не правильнее было переделать попытку (или добавить в коде попытки) условие на ФайлСуществует?
Странно, выше говорится о проблеме выборки в запросе данных с типом ХранилищеЗначений, а в качестве объяснения непонятно к чему приводится пример с выборкой ссылок из справочника номенклатуры. Разбитие запроса на порции может и не решить конкретно в вашем случае проблемы с потреблением памяти. В результате проблема решается как положено, но к чему приведена информация о разбиении запроса на порции — не понятно.
P.S. За труды в любом случае «+» 🙂
off:
Вспомним Душелова …
RIP
(7) Прям в глаза бросилось 2019… как так подумал я
RIP
(7)
Я знал Васю лично, помним!(7)
(0) А еще есть такая штука как MS SQL Server и план запроса, где видно задействует-ли он для получения данных отличный оператор как
Spool и если база Tempdb на этом же диске тогда вообще я не вижу никаких аномалий и странностей.
Можно подключить файлопомойку террабайта на 2
(из двух SATA-шников в R1, какого-нибудь тырпрайз уровня, которые заявляются как 247 с большой устойчивостью к вибрациям)
и слинковать туда толстые temp через mklink /J
Или целиком перенести туда профили.
И износ системного харда уменьшим, и место уменьшаться перестанет так стремительно.
(6)
По объему записи/чтения диска
Да, действительно плохо, переделал.
К сожалению это единственное место, где я смог найти упоминание о свопе результатов запроса на диск (