Заметки: 1С

Мои мысли о жизни, работе и разных интересных штуках вокруг меня.

Показано: 0 · Страница

Номер года в литерале типа дата превышает 3999
28 марта 2026 ·

Про ошибку в названии заметки я уже как-то писал. Напомню суть: платформа не переваривает даты позже 3999 года (на практике начинает стрелять с 3999-12-01). Теоретически такие даты вообще нельзя записать, но если очень хочется из-за ошибок в прикладном коде (и в коде самой платформы) это всё-таки возможно и время от времени случается.

Обычные симптомы — перестают работать какие-то отчёты, не проводятся какие-то документы, падает пересчёт итогов. В общем, страдает любой код, который трогает записи с проблемными датами.

Способ лечения, который я рамочно описал в заметке по ссылке выше — рабочий, но сравнительно медленный: нужно настроить ТЖ, собрать и распарсить результаты. Между тем, платформа падает на первом же обращении к битой дате, а их может быть много и валяться они могут в разных таблицах. То есть может потребоваться несколько подходов к снаряду: проверили, получили ошибку, исправили, проверили, получили следующую...

Thank You, Mario!

Желая решать проблему как-нибудь побыстрее, несколько лет назад я написал запрос для PostgreSQL. Общая идея:

  1. Ищем в базе данных все поля с датами.
  2. Строим мегазапрос к таким полям (ищем даты, которые выходят за лимит 1С).

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

Почему действуем на уровне СУБД? Ну, средствами 1С такой номер не отколоть — напомню, платформа падает при попытке потрогать проблемные записи. Это касается любого взаимодействия, включая чтение. Кроме того, в данном случае работать напрямую быстрее и удобнее.

На днях я переписал этот запрос для MS SQL. Вышло длиннее (в силу особенностей этой СУБД), но идея та же.

Если будете использовать, имейте в виду, что:

  • Поле _Fld626 в тексте запроса — разделитель для фреша. В вашей базе может называться по-другому или вообще отсутствовать.
  • Запрос написан для базы со сдвигом в 2000 лет. Если в вашей базе сдвига нет, нужно скорректировать условие (см. функцию DATEADD()).
  • Трюк с выводом в XML (см. FOR XML) я добавил, чтобы не дать SSMS обрезать текст мегазапроса (показалось быстрее, чем возиться с приведением типов). Побочный эффект: в получившемся мегазапросе нужно заменить > на >, прежде чем выполнять его.
С побочным эффектом
15 марта 2026 ·

В последнем релизе нашей ERP мы оптимизировали несколько тяжелых динамических списков (заказы, инвойсы, проформа инвойсы и так далее). Там накопился приличный техдолг, в основном — горы вспомогательных таблиц, прикрученных к основным запросам (контактная информация, технические реквизиты, остатки, обороты и так далее). В итоге даже в сравнительно небольших приложениях оптимизатор СУБД начинал выдавать бредни вместо планов запроса и мало-помалу платить за это ресурсами стало совсем неприятно.

В общем, запатчили через несколько разных подходов, один из них — подгрузка дополнительных данных в обработчике ПриПолученииДанныхНаСервере() (я про него недавно вспоминал, кстати). Написали удобный фреймворк вокруг фичи, внедрили, потестировали и... В общем, сидим с кислыми минами.

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

Например, вводим значение, которое видим в колонке, а строка не находится. Или находятся не все строки. Или находятся строки, которые не должны были найтись. Для пользователя это выглядит отвратительно, ну баг и баг же: визуально-то такие поля никак не отличаются. Как ты ему объяснишь, что это особенность работы механизма платформы™?

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

Блин, вот как можно было разработать такой замечательный концепт обработчика и настолько заруинить реализацию на уровне платформы?

Неожиданно вспомнил Реддит. В некоторых сообществах популярны треды «придумай суперсилу, но с побочным эффектом». Типа, кто-то пишет в комментариях: я могу бегать со скоростью ветра! Ему отвечают: да, но не можешь тормозить. И так далее. Местами получается забавно.

Superpower

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

Управление бэкапами
6 декабря 2025 ·

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

У нас уже было решение на базе UI Битрикса, но в силу, э-э, особенностей развития этого продукта оно приносило больше боли, чем пользы. Поэтому мы переосмыслили процесс и всё переписали. На фронте — 1C, на бэке — PostgREST, PostgreSQL, PowerShell и много чего ещё. Логика довольно сложная, но у пользователя — простой и дружелюбный UI, через который можно заказать бэкап буквально в два нажатия.

Выбрать можно один из трёх видов бэкапов:

  • облачный (копия реального приложения, развёрнутая в облаке и доступная, в том числе, через браузер);
  • файловый бэкап (обычный .dt-файл, который можно скачать и развернуть на локальной машине);
  • бэкап конфигурации и расширений (.cf + .cfe).

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

Ну и продолжаем хохмить в интерфейсе, конечно.

But Still!

Coffee First!

Протоптанные дорожки
29 ноября 2025 ·

Ладно, загадка Жана Фреско. У вас есть таблица, скажем, на 50 тысяч строк. Как прочитать из неё полмиллиарда?

Раз плюнуть, Nested Loops + Clustered Index Seek:

Полмиллиарда

От Clustered Index Seek тут одно название, конечно. Фактически оператор при каждом исполнении пробегает по всей таблице (всему кластерному индексу) и сверяет каждую запись с Predicates. И так — 10 730 раз для 51 391 записей. В итоге 551 425 430 строк прочитали, 13 343 вернули.

Ох

Короче, идеальный пример плохого плана запроса в вакууме, хоть сейчас тащи в палату мер и весов. Nested Loops, если кто позабыл, работает примерно так:

For Each Table1Row In Table1 Do
    For Each Table2Row In Table2 Do
        ...

Это ОК для мелких таблиц, но СУБД может его применить и для таблиц поболбше — например, если ей не хватает времени на построение плана.

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

Некоторые регистры и сами по себе были здоровенными, а виртуальные таблицы дополнительно поддали жару (каждая превращается в 2+ вложенных запроса). СУБД честно пыталась придумать эффективный алгоритм, но в какой-то момент решала, что хреновый план запроса всё же лучше, чем вообще никакого.

В итоге пользователь что? Пытался поискать документ по номеру и клиентское приложение просто-напросто зависало.

Короче, по поводу виртуальных таблиц в динамических списках. В английском есть выражение «desire path», «протоптанная дорожка». Часто прицепить виртуальную таблицу к основной — и в самом деле самый простой, быстрый и привычный способ решить задачу. Но он не эффективен.

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

Безвредный вред
16 ноября 2025 ·

Разбирали на днях с коллегой проблему. Ничего особенно серьёзного, очередное расследование вида «какого черта этот запрос ведет себя странно?».

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

Запрос был примерно такой:

SELECT
    Table.Field1 AS Field1
FROM
    Table AS Table
WHERE 
    &Parameter

Если нужно было отбирать записи из исходной таблицы во временную, в параметр передавался TRUE; если временную таблицу нужно было получить пустой — передавался FALSE.

Несмотря на кажущуюся простоту, такой трюк — проблема для производительности, если таблица, которую читает запрос — большая.

Причина в том, как СУБД работают с параметризованными запросами. И MS SQL, и PostgreSQL строят план выполнения запроса на основе его текста, и в примере выше значение параметра не повлияет на принятие решения, нужно читать таблицу или нет.

Таким образом, при выполнении такого запроса обе СУБД педантично прочитают всю таблицу (ну, или её индекс), даже если параметр равен FALSE. В последнем случае каждая прочитанная запись будет отброшена и алгоритм будет работать корректно, однако мы будем тратить ресурсы на бессмысленное чтение данных и забивать буферный кэш, замедляя систему в целом и активно работая на глобальное потепление :)

Решение тут простое — вставлять TRUE/FALSE в тело запроса как константу, не используя параметр. Либо использовать оператор TOP, так текст запроса будет даже проще:

SELECT TOP 0
    Table.Field1 AS Field1
FROM
    Table AS Table

Тут на уровне SQL мы получим что-то вроде «SELECT TOP 0 ... FROM Table» (для MS SQL) и «SELECT ... FROM Table LIMIT 0» (для PostgreSQL). В итоговом плане будет оператор чтения, но исполнитель фактически не запросит ни одной строки, так что реального сканирования данных не случится (ура).

P.S. Если не критично получать во временной таблице корректные типы колонок, можно вообще вот так:

SELECT TOP 0
    UNDEFINED AS Field1

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

Шаманство
5 октября 2025 ·

Поймали, кажется, первый воспроизводимый в лабораторных условиях сценарий повреждения кэша платформы. Короткий синопсис:

  • Создаем новое приложение из шаблона 35-го релиза нашей ERP.
  • Запускаем его и ждем, когда закончится инициализация.
  • Заменяем конфигурацию приложения на 36-й релиз (конкретно, версию 28537 из хранилища разработки) и снова запускаем.

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

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

Welcome To Dipshit Central

Как и всегда, когда начинается магия — проблему нужно искать в кэше. Чистишь — все симптомы бесследно исчезают. Кроме того, есть и косвенные признаки:

  • Перечисление было и в 35-м релизе, но было переименовано в 36-м;
  • Методов в 35-м релизе вообще не было (в 36-м их как раз разработали).

То есть не первого, не вторых в кэше 35-го релиза не существовало, а платформа, по какой-то причине, пытается рыться в именно в нём.

Мы пока не доискались, что вызывает сбой. Вышли на конкретный коммит, после которого появилась проблема, но в нём из необычного — одно название (и то поблизости есть куда более подозрительные с этой точки зрения кандидаты). В остальном там минорные изменения, сопровождаемые заменой внутренних идентификаторов объекта метаданных. Они, конечно, связаны с ключами кэша, но их сброс делается при любом изменении любых метаданных и никогда раньше не приводил к проблемам.

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

Шаманство, конечно, но работает ¯\_(ツ)_/¯

Медленное удаление областей Фреша
3 августа 2025 ·

Коллега заметил, что на одном из инстансов нашего фреша удаление областей стало идти прямо-таки трагически долго. Что в метриках? Вот это:

DELETE FROM T1
FROM _DataHistoryMetadata T1
WHERE 
    T1._MetadataId = ?
    AND T1._IsActual = 0x00 
    AND NOT (
        T1._MetadataVersionNumber IN (
            SELECT T2._MetadataVersionNumber AS MetadataVersionNumber_
            FROM _DataHistoryVersions T2
            WHERE T2._HistoryDataId IN (
                SELECT DataHistoryLatestVersions1.DataHistoryLatestVersions._HistoryDataId AS HistoryDataId_
                FROM DataHistoryLatestVersions1.DataHistoryLatestVersions T3
                WHERE DataHistoryLatestVersions1.DataHistoryLatestVersions._MetadataId = ?
            )
        )
    )

Каждый такой запрос читает порядка двадцати гигабайт. Что тут происходит — примерно понятно: платформа пытается удалить историю данных области и кривой запрос к БД приводит к сканированию всей таблицы по всем областям вместо быстрого поиска по индексу. Кто-то на Дмитровском опять поленился.

Чему ты удивлён?

В общем, теряем от тридцати секунд до получаса на действие. Хотелось бы побыстрее. Решение:

CREATE NONCLUSTERED INDEX IX_DataHistoryLatestVersions1_MetadataId
  ON dbo._DataHistoryLatestVersions1 (_MetadataId)
  INCLUDE (_HistoryDataId)
  WITH (DROP_EXISTING = OFF, ONLINE = OFF);

CREATE NONCLUSTERED INDEX IX_DataHistoryVersions_MetadataVersionNumber
  ON dbo._DataHistoryVersions (_MetadataVersionNumber)
  WITH (DROP_EXISTING = OFF, ONLINE = OFF);

Стоимость удаления области ожидаемо просела на ~99%.

Если будете повторять у себя:

  1. Формально такой трюк нарушает лицензионное соглашение, you've been warned и всё такое.
  2. Есть риск, что платформа при будущих реструктуризациях будет спотыкаться о новые индексы (особенно при работе по «новой» схеме). Лучше иметь под рукой готовый скрипт, который сможет их грохнуть, а потом (после реструктуризации) — вернуть обратно.
Развенчиватель мифов
27 июля 2025 ·

Попался под руку объёмный текст на Инфостарте про мифы о платформе, полез читать.

По сути. Половина там написана заголовка ради, кажется. Свежий номер! Криминал, интриги, расследования! Файловая база быстрее клиент-серверной! СКД медленнее запроса! Вызов метода сервера на сервере — новый вызов сервера! НЛО над Красной площадью!

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

Или, скажем, сравнение скорости выгрузки в таблицу для представлений объектов и для их наименований. Первое — в десятки раз медленнее! На первый взгляд странно: в конце концов, в обоих случаях у нас строки, и на момент замера они уже извлечены из базы. Думаю, что дело в типизации: строки представлений имеют неограниченную длину, в отличие от наименований. Отсюда расходы на выделение памяти, какие-то вспомогательные структуры и вот мы имеем то, что имеем.

P.S. Про некоторые аспекты из статьи я где-то у себя писал. Если навскидку — помню свою попытку измерить скорость работы ValueIsFilled() и неприятный сюрприз от встроенного FindByNumber().

Нет
11 июня 2025 ·

Нет

Сообщение, которое выдает актуальная платформа при попытке подключения к выключенному кластеру серверов. Я, конечно, люблю лаконичность в интерфейсах, но тут всё-таки перебор.

Платформа, причем, установлена с единственным языковым пакетом (английским) и даже запущена с жестким указанием на «Ven VLen», но откуда-то всё равно прорастают родные березки. Возможно, сишная библиотека, откуда стреляет сообщение (DataExchangeTcpClientImpl.cpp) в моем случае смотрит на язык ОС (русский), сложно сказать.

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

No

Немного хирургии
17 декабря 2024 ·

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

А сегодня с коллегой ремонтировали кластер 1С с базами на 1cFresh, которому после миграции на соседний сервер внезапно поплохело (если вкратце, при попытке распечатать документ клиентское приложение умирало в муках). Пока возились, мелькнула мысль: это, конечно, не так жутко выглядит, как ремонтировать софт, от которого вот прямо сейчас зависит чья-то жизнь, но... Если посчитать всех клиентов, которые вот прямо сейчас сидят на нервах из-за того, что у них бизнес стоит — ещё неизвестно, где уровень стресса будет выше.

P.S. Технические заморочки для тех, кому интересно. Вышло так, что при миграции кластера права на папку с серверным кэшем переехали некорректно и это привело к любопытному эффекту: в ней оседали только логи, а данные сеансов — нет. В итоге при открытии печатной формы документа конфигурация пыталась положить её в хранилище; rphost, в свою очередь, пытался засунуть её в серверный кэш сеанса.

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

Пересобрали права, перезапустили кластер, проблема ушла.

End of Report

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

Восторг
31 августа 2024 ·

Помню, в каком восторге я был, когда впервые поработал с оконными функциями в PostgreSQL. Можно построчно регулировать окошко вычислений для каждой строки! И на сегменты предварительно нарезать! И всё это — нативно, внутри самого обычного запроса, никаких пристроек справа и слева. Стандартная агрегация с группировкой и подзапросами, к которой я привык в 1С, резко превратилась в тыкву, совсем как карета Золушки в полночь.

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

Технологии удивительны. Учиться — клёво. Ради таких озарений хочется делать это ещё и ещё :)

Некуда бежать
31 августа 2024 ·

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

Ой, привет!

Отвлекся, блин. Платформа следит за тобой, %username%!

P.S. Зато теперь у меня есть кастомный значок с символикой любимой компании!

Яга
29 июля 2024 ·

Случайно вспомнил, что полгода назад кто-то из коллег закинул в рабочий чат ссылку на Ягу — будущего (наверное) конкурента JIRA в России. Разработкой, судя по URL, занимаются ребята из Ростелекома. Проснулось любопытство — как оно там, взлетело?

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

Нейминг, конечно, у ребят кликбейтный. Но не уверен, что прям удачный. Например, название «1С», может, и провоцирует натужные шутки (один эс! задница Одина! хахаха!), но если ты гуглишь его — то, скорее, находишь инфу про платформу или хотя бы вендора. А не кучу статей, фанфиков, фанарта и видеоигр, как в случае с Ягой.

И вообще, ассоциации какие-то не те лезут. Избушка на курьих ножках? Выглядит как изначально плохо спроектированное приложение, которое кое-как переделали прямо на проде. Ступа? Костыль? Блин, если смотреть с точки зрения программистов — тут не красные флаги, а алые знамена :)

Ну и я еще, наверное, испорченный, но при слове «Яга» у меня в голове первым делом всплывает не могущественная ведьма из славянского фольклора, а отвратительное пойло из двухтысячных.

И вот баба с ягой уже не быдло, а патриотичная программистка!

― коллега

Не уникальные метаданные
8 июня 2024 ·

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

Внешне он выглядит так: вы обновляете конфигурацию базы, и при попытке реструктуризации выскакивает ошибка "Таблица метаданных истории данных cодержит не уникальные записи, которые должны быть удалены".

При этом платформа не предлагает никакого понятного способа найти такие записи — иди туда, не знаю куда, сделай то, не знаю что.

Таблица метаданных истории данных cодержит не уникальные записи, которые должны быть удалены

Проблему можно решить, порывшись в базе данных. Таблица, на которую ссылается ошибка — _DataHistoryMetadata. В ней лежат версии метаданных каждого объекта, для которого ведётся история. Это позволяет платформе понимать, какие реквизиты были у объекта на любой момент времени, в течении которого для объекта велась история.

Как это работает? Ну, когда меняется состав реквизитов объекта (например, реквизит в справочнике добавили), платформа запоминает его метаданные: конкретно, добавляет в _DataHistoryMetadata новую запись и сохраняет в ней актуальный список реквизитов объекта, а также номер версии этого списка (например, в при включении истории для объекта сохраняется первая версия метаданных, при добавлении какого-нибудь реквизита — вторая и так далее).

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

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

Как помочь больной скотине?

Решение вытекает из алгоритма выше: нужно найти конфликтующие версии и отобрать признак актуальности у той, что старше. Лучше использовать запросы: историю данных включают, как правило, для кучи объектов и состав реквизитов у них постоянно меняется — в общем, версий в таблице будет столько, что черт ногу сломит.

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

  1. get-issues.sql проверяет, что проблема есть: ищет версии метаданных, которые одновременно помечены как актуальные.
  2. fix-issues.sql снимает признак актуальности с тех версий, которые на самом деле устарели.

Оба запроса написаны для Microsoft SQL Server. Если вы используете PostgreSQL, то вот они же для этой СУБД.

Запросы потребуют небольшой адаптации под конкретную базу: в них используется поле _fld626, в котором хранится разделитель данных. В вашей таблице _DataHistoryMetadata это поле может называться иначе, поэтому нужно обновить его имя на актуальное. Ошибиться будет трудно — у таблицы только одно поле с префиксом _fld.

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

Главная проблема UUID
2 июня 2024 ·

Наткнулся на хороший текст об основной проблеме, которую таскает с собой UUID. Для 1С она тоже актуальна: все ссылочные объекты платформы (элементы справочников, документы и так далее) имеют собственные UUID. Они хранятся в БД, активно используются при поиске и, понятно, обильно индексируются (со всеми вытекающими последствиями).

1С старается компенсировать проблему, создавая последовательные UUID. Пусть не идеально, но в целом эта штука работает и индексы получаются более-менее ровными. Да и вообще в сообществе об этом говорят довольно давно: вот, например, бородатый топик на Мисте (правда, тут диалог быстро перерос в курятник и из шести десятков комментариев от силы полтора — по делу).

P.S. Рассмешила ремарка про вероятность создать в одной базе два одинаковых UUID:

As an aside, for those worried about collisions: you should take up the lottery, since winning the jackpot twice in a row is a much more likely outcome than your system ever generating two identical random 128 bit numbers.

Запросом больше, запросом меньше
5 мая 2024 ·

Регулярно слышу тейк: запросом больше, запросом меньше — без разницы. Мол, главное, чтобы запрос был дешевым: не читал лишнего, попадал в индекс и так далее.

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

Пример из недавней практики. Есть ERP, в которой лежит таблица с этапами оплаты по заказам клиентов. Один из таких этапов — предоплата; пока она не внесена, создать заказ поставщику нельзя.

Технически в заказе поставщику просто хранится ID заказа клиента; если последний заполнен (то есть, заказ поставщику создан под заказ клиента), ERP нужно прочитать этапы оплаты по заказу клиента и понять, можно ли делать закупку.

Звучит элементарно, однако мониторинг показывает: операция тормозит и жрет память, будто в последний раз. Лезем разбираться. Видим примерно такую картинку:

825701 записей

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

Опуская детали: проблема рождалась в тех заказах поставщику, которые вообще не были связаны с заказом клиента. Разработчик посчитал, что для них можно не менять логику: ID заказа клиента пустой и запрос не найдет для него этапов оплаты. А значит, получится тот же результат, как если бы запроса вообще не было. А лишний запрос — ну... Запросом больше, запросом меньше... Тоже мне, большое дело.

Оказалось, большое. В таблице этапов оплаты оказались данные не только для заказов клиентов, но и для других видов документов. Поле с ID заказа клиента у них было пустым. В итоге ERP при попытке найти этапы оплаты по пустому ID заказа клиента находила такие записи — и, как видите, немало.

Запрос читал порядка гигабайта данных и помещал во временную таблицу. Гигабайт прочитали, гигабайт записали... История била и по диску, и по буферному кэшу СУБД, и по другим частям системы (вплоть до сети, которой этот гигабайт приходилось гонять туда-сюда без всякой пользы).

В общем, знаете, что я думаю? Если результат запроса известен — наверное, его все-таки не нужно делать.

Не только лишь все
25 февраля 2024 ·

Завидной глубины комментарий из документации к методу WriteJSON() XDTOSerializer'а:

Не только лиь все

Ну да, метод дампит данные в JSON, а не в XML. Так что поспорить сложно, не все типы данных можно упаковать в XML с его помощью (если быть точным — никакие). Если бы дальше не пошла очевидная копипаста из справки к WriteXML() — счёл бы пасхалкой от разработчиков :)

Последний мет
17 декабря 2023 ·

Роюсь в коде внешней компоненты 1С, опубликованный разработчиками платформы как пример у себя на сайте. Из хорошего: ну, она компилируется и, если немного допилить — действительно работает.

В остальном хватает bruh moments: например, проект не открывается в современной Visual Studio (нужно указывать CMake вручную). Код довольно небрежный, документации нет, комментариев и оформления по большому счёту тоже. Разработчику без опыта в С++ может быть непросто вкатиться.

Позабавил нейминг:

long CAddInNative::FindMethod(const WCHAR_T* wsMethodName)
{ 
    long plMethodNum = -1;
    wchar_t* name = 0;

    ::convFromShortWchar(&name, wsMethodName);

    plMethodNum = findName(g_MethodNames, name, eMethLast);

    if (plMethodNum == -1)
        plMethodNum = findName(g_MethodNamesRu, name, eMethLast);

    delete[] name;

    return plMethodNum;
}

Со строк выше на нас смотрит необъяснимая любовь автора кода к сокращениям: вот что ему мешало назвать переменную "eMethodLast", а не "eMethLast"? В конце концов, у нас уже есть "wsMethodName" и "plMethodNum".

Возможно, это такая пасхалка с отсылкой на Breaking Bad. Тогда, конечно, уверенный лайк :)

Бытовой героизм
24 сентября 2023 ·

Поднимал какое-то время назад Swagger для внутренней API-шки. Пока возился — стало понятно, что часть функционала в документацию включать не нужно. Поискал способ, как это сделать без костылей — наткнулся на забавный вопрос на GitHub'е.

Чем забавный? Ну, невольно вспомнил про Мисту. В среде 1С-разработчиков это синоним слова "токсичность": мол, спросишь там что-нибудь — получишь ушат помоев за шиворот вместо ответа. Здесь, конечно, не так всё запущено, но ребята, с завидным упорством ссылающиеся на 14-и страничный мануал, здорово рассмешили.

Одно радует: к концу треда всё же нашёлся отважный повстанец, который просто взял и запостил нужный параметр для декоратора.

Не все герои носят плащи, ей-богу.

Таков путь
17 июля 2023 ·

Немного экзистенциальной безысходности от СП (на случай, если вам её почему-то не хватало):

Исключение генерируется

Это справка для метода УдалитьСообщения() менеджера канала сервиса интеграции. Если серьёзно, я понимаю, что тут имеется в виду: дело в том, что все методы сервисов интеграции швыряются исключениями, если их вызвать в сеансе со включенными разделителями. Авторы документации педантично сообщают об этом в справке по любому из них, так что здесь, видимо, просто почему-то кусок отвалился. В английской документации к этому методу, например, всё нормально.

Но все равно, забавно смотрится. Напомнило старый мем:

Исключение генерируется