Заметки

Протоптанные дорожки

Ладно, загадка Жана Фреско. У вас есть таблица, скажем, на 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», «протоптанная дорожка». Часто прицепить виртуальную таблицу к основной — и в самом деле самый простой, быстрый и привычный способ решить задачу. Но он не эффективен.

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

29 ноября 2025 работа оптимизация

Дневник питания в Obsidian Bases

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

Ну и симпатичная, да:

UI

Все необходимые настройки и скрипты — в репозитории на Github'е; инструкция переведена на русский язык.

23 ноября 2025 готово Obsidian

Ну, есть кое-какие

Гуляю вечером, сзади идёт какая-то мама и её мелкий — лет пяти, наверное. Я их не вижу, просто слышу разговор. Мама объясняет ребенку про университет: мол, туда надо поступить, учиться, будут экзамены и всё такое.

Мальчик молчит, потом расстроенно выдает:

— Я думал, есть только школа, а оказывается есть ещё сложности...

17 ноября 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

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

16 ноября 2025 PostgreSQL MS SQL

Шаманство

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

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

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

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

Welcome To Dipshit Central

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

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

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

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

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

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

5 октября 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. Есть риск, что платформа при будущих реструктуризациях будет спотыкаться о новые индексы (особенно при работе по «новой» схеме). Лучше иметь под рукой готовый скрипт, который сможет их грохнуть, а потом (после реструктуризации) — вернуть обратно.

3 августа 2025 работа

Развенчиватель мифов

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

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

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

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

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

27 июля 2025

Удаление геометрии

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

Демо

Идея довольно простая: сводим геометрию объекта-ограничителя к кубу, вычисляем его минимальные и максимальные координаты по всем осям, а потом для каждой точки «обрезаемого» объекта проверяем, входит ли она в полученный диапазон. Входит? Удаляем.

Способов применения — масса; например, мне это пригодилось, когда я захотел автоматически отрезать часть линий декоративного нотного стана на стене так, чтобы они не пересекали дверной проем.

Пример

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

12 июня 2025 3D

Нет

Нет

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

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

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

No

11 июня 2025 работа

Проблема джинна

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

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

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

— Аппарат испорчен, — объяснил он Хоттабычу, — сейчас я открою крышку. Посмотрим, в чем там дело.

Но коробка аппарата, несмотря на все усилия Вольки, никак не открывалась.

— Он сделан из цельного куска самого отборного черного мрамора! — похвастался Хоттабыч.

— Значит, внутри там ничего нет? — разочарованно спросил Волька.

— А разве внутри должно что-нибудь быть? — забеспокоился Хоттабыч.

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

«Старик Хоттабыч», Лазарь Лагин

Занимаясь 3D-моделированием, регулярно ощущаю себя этим джинном. Старик тоже пытался повторять форму предметов и даже делал их симпатичнее, как умел — однако фейлился, так как не понимал, что там внутри.

Вот и я так же. Последний месяц довожу до ума модель школьного класса музыки и знаете что? Об устройстве рояля я уже узнал больше, чем за всю предыдущую жизнь. А ещё могу прочитать короткую лекцию о системах отопления в российских школах: секционных батареях, панельных, чугунных и алюминиевых, какие чаще встречаются, расстояниях между ними, почему не надо подключать три в ряд и так далее :)

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

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

12 апреля 2025 3D

Пасхалки

Thank you, Mario! Awaiting Signal

Пара пасхалок, которые можно найти внутри FirstBit ERP. Спрятаны для разработчиков: пользователи в первом случае видят на форме заголовок и текст оповещения, а во втором — стандартное waiting for connection.

Люблю время от времени делать такие штуки: это весело, а ещё — помогает не скучать, даже если задача не слишком увлекает или просто устал. Если не читали заметку «Дизайн без стресса» Быстроновского — прочитайте, она клёвая (а я все равно не сформулирую лучше).

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

Nice try, Marty!

6 апреля 2025 работа

Разработка ERP на английском

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

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

19 января 2025 работа английский

Загадочный код

Код

Где-то на улицах Тбилиси. Как большой фанат серии Dishonored — не смог пройти мимо. Уверен, поблизости есть сейф, к которому этот код подходит!

18 января 2025 Грузия видеоигры

Уходи, негатив!

Чичилаки

Белые штуки на заднем плане называются чичилаки. Это что-то вроде грузинской новогодней ёлки (де-факто, тщательно наструганный орешник). Считается, что он собирает на себя весь накопившийся негатив, так что после праздников его положено сжечь (вместе с негативом).

Последнее, как оказалось, задача со звездочкой. Местная полиция (и особенно пожарка) относится к этой старой доброй традиции с подозрением, и их можно понять (чичилаки бывает и в рост человека, факел выйдет — будь здоров). Так что «заряженную» негативом чичилаки нам посоветовали просто выкинуть в мусорку, чтобы не нажить свежих проблем. Или сдать в зоопарк: животные ими играют и зубы чистят.

Последнее позабавило. Избавиться от негатива помогают котики, какая неожиданность. Да, здоровенные и повышенной зубастости, но всё же!

29 декабря 2024 Грузия тем временем

Немного хирургии

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

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

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

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

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

End of Report

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

17 декабря 2024 работа

Смутно знакомый логотип

Тем временем OpenAI открыл в Тбилиси филиал На самом деле тут вейпами торгуют, а дизайнер логотипа, скорее всего, вдохновлялся клубами пара. Но каждый раз, как мимо хожу, глаз цепляется :)

Vaporia

14 декабря 2024 ИИ Грузия

Жуткое кладбище

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

Creepy Graveyard

Проблем, конечно, хватает: и в моделях, и в цветах, и в композиции... Да вообще везде. Хорошо хоть, что сцена по сюжету — ночь и туман. Самые косячные места можно просто спрятать. Не удивлюсь, если авторы курса специально выбрали такой сеттинг, чтобы новички не расстраивались :)

Но результатом я всё равно доволен. Думаю, на этом этапе перфекционизм скорее мешает, чем помогает. К тому же получилось явно лучше, чем прошлая работа. Я тогда был совсем зелёным в Blender и боялся сделать шаг в сторону от преподавателя: десять минут смотрел урок, а потом десять минут пытался повторить.

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

Короче, прогресс есть, едем дальше!

UPD: Один из фаундеров FlippedNormals записал ролик с теми же мыслями про обучение.

26 ноября 2024 3D готово

Сводка в Slack

Попался под руку скрипт, который я пару лет назад накатал для рабочего GitLab'а. Вкратце: мы ежедневно прогоняем репозиторий разработки через прорву тестов на Ванессе, в результате чего получаем симпатичный отчет — сколько тестов выполнено, какие провалились, причины провалов и так далее.

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

Чтобы упростить эту рутину, я немного расширил код пайплайна: после формирования отчета GitLab первым делом создает короткую сводку (тип клиента, тип базы, статистика по тестам) и отправляет ее в слак.

Отчет

Как бонус, стало проще отвечать на философский вопрос «кто все сломал». Чаще всего — автор первого коммита, на котором метрика Scenarios Failed на скриншоте выше пробила потолок :)

10 ноября 2024 работа готово PowerShell

Лог и баг

Люблю иногда покопаться в происхождении слов. Например, «лог» пришло из морского дела и означало буквально бревно: деревянный поплавок, к которому прикрепляли веревку и бросали за борт судна. Верёвка имела узлы на равных промежутках, и по числу узлов, которые успевали размотаться за определённое время, моряки определяли скорость судна.

Эти измерения записывались в судовом журнале, который тоже назывался логом. Со временем так стали называть вообще любой журнал или запись о каком-то событии, а в айти слово вообще обрело вторую жизнь :)

Или, скажем, «баг» — ну, про это, наверное, все слышали? Мотылек, который семьдесят с гаком лет назад помешал работе компьютера, был отмечен в журнале как «first actual case of bug being found», и пошло-поехало. Насекомое получило своего рода цифровое бессмертие: теперь так называют любую ошибку, особенно в программе.

(Жаль только, что это было не какое-нибудь насекомое посимпатичнее — ну, не знаю, бабочка? Это прибавило бы нашей работе нотку романтики)

(Впрочем, если подумать... Может, «bug» и не такой плохой вариант. «Butterfly» — как-то длинновато. Кроме того, кто-нибудь быстро сократил бы это до «butt», и чинили бы мы сейчас не баги, а жопы)

12 октября 2024 английский

Мимикрия ChatGPT

Коллеги вовсю экспериментируют с o1-preview от OpenAI: модель получилась по-настоящему интересной. Жаль, задачи для неё попадаются не так уж часто: с большинством повседневных прекрасно справляется и обычная 4o. Поиск ошибок в коде, анализ медицинских тестов на незнакомом языке, попытки вспомнить название книги, которую читал в детстве и едва помнишь завязку — я сходу и не вспомню, что ей не по зубам.

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

Морковка Кулаки Угараешь?

А вот ответ ниже прямо порадовал. Даже если оставить за скобками философию — Скайнет, похоже, отменяется! :) Впрочем, будущее из финала SOMA — пока, боюсь, нет.

Любовь

29 сентября 2024 ИИ видеоигры

Ранее Ctrl + ↓