Заметки

Позднее Ctrl + ↑

Новый скрипт топа исключений

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

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

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

grep -hoP ",EXCP,.*\KDescr=.*" */*.log | uniq -c | sort -rn

То есть фильтруем только строки с событием EXCP, отрезаем всё до описания ошибки и группируем с помощью uniq. По-моему, очень изящно.

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

17 октября 2020 bash готово

Уязвимости в конфигурациях 1С

В прошлом месяце был на митапе «Инфостарта» по безопасности решений на платформе 1С. Узнал кучу интересного! Среди прочего, Олег Тымко обзорно рассказывал про подходы в разработке, которые можно считать потенциальными уязвимостями продуктов. Например, зашитых прямо в код IP-адресов, ссылок, e-mail'ов, паролей и так далее.

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

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

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

13 октября 2020 bash готово

Про Single Sign-On

Твит

Этот эпизод схватки двух йокодзун конфликта между Apple и Epic Games, безотносительно всего прочего — отличное напоминание, что Single Sign-On в интернете использовать нельзя: нигде, никогда, ни на каких сервисах. Неизвестно, какие ещё гиганты внезапно сойдутся на кулачках или какой сайт забанит тебя без всякой внятной причины. Because screw you, dude, that's why.

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

Решений, по удобству ничем не уступающих Single Sign-On, полным-полно. Менеджеры паролей, разнообразные расширения для браузеров, «железные» ключи — да что угодно будет лучше, чем проприетарные сервисы с закрытым кодом, на которые вы не имеете никакого влияния.

12 сентября 2020 тем временем

Профессионал по техвопросам

В конце августа сдал тест на профессионала 1С по техническим вопросам крупных внедрений. Это по сути такой входной билет на основной экзамен, призванный проредить поток претендентов и заставить их подучить теорию.

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

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

5 сентября 2020 готово

Неразрешимые ссылки на функциональные опции

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

CommonForm.PersonalSettings.Form Unresolved metadata object references (2)
Catalog.BankAccounts.Form.GLAccountsEditForm.Form Unresolved metadata object references (1)
Catalog.CashRegisters.Form.GLAccountsEditForm.Form Unresolved metadata object references (1)

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

Бегло просматриваю файл в поисках чего-то необычного. Искать долго не приходится:

Подозреваемый

Нормальная ссылка на функциональную опцию — её имя (как в случае DepreciationOfAssets). А вот если вместо имени указан GUID — этой функциональной опции в конфигурации нет. Ссылка неразрешима.

Делаю поиск этого GUID'а по остальной выгрузке и нахожу почти все проблемы, на которые ссылалась платформа при проверке. Решить их легко: удалить битую ссылку из XML, а потом загрузить файл обратно в конфигурацию. Можно ещё проще: открыть список функциональных опций для элемента формы и тут же сохранить его. В этом случая битая ссылка также будет удалена.

Почему искать таких потеряшек через сам Конфигуратор — занятие для клинических оптимистов? Да просто проблему почти невозможно заметить. В лучшем случае вы увидите что-то в духе:

Настоящая улика

Конфигуратор понимает, что в поле две опции, но получить название для второй не может (её нет). А ведь чаще всего функциональная опция только одна! И картина выглядит так:

Ваша честь, я невиновен!

19 августа 2020

Метод с сюрпризом

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

SELECT Ref FROM Document.Invoice WHERE Number = &Number

А вот так — фигушки:

Documents.Invoice.FindByNumber(Number)

Сначала я даже слегка завис, но потом полез в документацию и, конечно, нашел ответ. У метода FindByNumber() есть второй параметр, IntervalDate, нужный для поиска периодического документа. С помощью него можно сузить поиск до конкретного периода; например, если периодичность нумерации — год и мы присвоим параметру значение 01-05-2020, то поиск пойдет в периоде от 01-01-2020 до 31-12-2020. А нумерация у документа Invoice и правда периодическая — в пределах года.

Так в чем проблема? Дело в том, что за уклончивым «the parameter is used for documents with periodic numbering» на самом деле скрывается железное правило: параметр нельзя опускать для периодических документов.

Чтобы убедиться в этом, посмотрим, какой SQL выполняется на стороне СУБД. Делаем обычный запрос — никаких сюрпризов. P1 здесь — разделитель, P2 — номер документа:

SELECT
    T1._IDRRef 
FROM
    dbo._Document283 T1
WHERE
    ((T1._Fld704 = @P1))
    AND ((T1._Number = @P2))

А вот если выполнить FindByNumber(), то в запросе появится третий параметр со значением 2001-12-31 23:59:59 — ну да, «конец первого года с начала времён»:

SELECT
    T1._IDRRef
FROM
    dbo._Document283 T1
WHERE
    ((T1._Fld704 = @P1))
    AND
    (
        T1._Number = @P2
        AND T1._Date_Time <= @P3
    )

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

Подведем итог. Как решить проблему — понятно: указываем IntervalDate или, если даты нет, подключаем ЗначениеРеквизитаОбъекта() БСП или его аналог. Но, честное слово, со стороны платформы, было бы куда адекватнее выбрасывать исключение, если FindByNumber() вызван без IntervalDate, а у документа включена периодичность — чем вот так, тихой сапой, делать гарантированно бессмысленные запросы.

27 июля 2020

Один запрос, что правит всеми

Истории ради закинул на Github Gist один из неоптимальных запросов, с которыми возился несколько месяцев назад. Он использовался для динамического списка в форме элемента справочника и, когда пользователь открывал вкладку с этим списком, платформа погружалась в медитацию даже в относительно небольшой базе.

На первый взгляд структура запроса простая и четкая: пачка запросов к таблицам документов, соединяемых через ОБЪЕДИНИТЬ ВСЁ. Каждая из таблиц фильтруется по примерно одинаковым условиям — тип ссылки, дата и вхождение ссылки в результат подзапроса.

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

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

WHERE VALUETYPE(AdditionalExpenses.Ref) IN (&DocumentsListSelectedTypes)

То есть в динамический список передается список типов документов, которые требуется вывести. Однако это условие будет наложено после выборки данных, и если пользователю нужны только инвойсы — СУБД все равно сначала выгребет все 14 таблиц, а потом отбросит 13 из них.

Но это всё не так критично. Если бы список проблем этим и ограничивался, мы, возможно, и не полезли бы разбираться. Главная проблема — во втором условии секций WHERE: каждый запрос проверяет вхождение ссылки на документ в результат подзапроса.

Использование вложенного запроса в условиях — само по себе почти табу, если речь идет не о временных таблицах: СУБД часто не в состоянии понять, сколько данных вернет подзапрос и, соответственно, какой способ работы с ними подойдет лучше. Однако тут это ещё и усугубляется тем, к какому источнику данных мы делаем запрос. Критерий отбора — это не таблица в базе данных, которую можно прочитать, пусть даже со сканом — это набор таблиц. В критерии отбора DocumentsByProject их тридцать!

В этом месте можно было бы сказать «занавес», но нужно добавить, что выборку из тридцати таблиц по определенному типу документа делает каждый из четырнадцати соединяемых запросов.

OMG

Вот теперь занавес :-)

25 июля 2020 готово оптимизация работа

ЭтотОбъект.ЭтотОбъект

Твит

Обожаю JavaScript! Каждый раз, когда у меня сгорает задница от какой-нибудь странной дичи в любимой платформе, я просто открываю любой тред про будни веб-девелоперов и быстро, очень быстро успокаиваюсь.

Впрочем, похожие трюки можно повторить в 1С. Например, JavaScript понимает window.window.window.location, а платформа — вот это :-)

24 июля 2020 вебдев JavaScript

Обновление запускатора служб

Доработал логику запускатора служб. Теперь, если вызвать скрипт без параметров (т.е. не указав ни -start, ни -stop) — он сам решит, запускать службы или останавливать их.

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

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

22 июля 2020 готово PowerShell рабочее место

Блог на Vue.js

В середине прошлого года я загорелся идеей переписать этот сайт на чем-то посовременнее PHP, выбрал Vue.js в качестве фреймворка и принялся за дело. JavaScript я на тот момент почти не знал, поэтому набил прорву шишек на кейсах в духе «метод вывода даты падает в Safari» — однако в итоге получил вполне жизнеспособное приложение.

Ну, как жизнеспособное? С одной стороны, главное у меня получилось — я разработал клиентское веб-приложение, которое ходит на сервер только за данными, а весь интерфейс собирает само. С другой — пришлось накрутить на фреймворк целую армию костылей хуков даже для самых простых штук (вроде подсветки элементов меню или, скажем, выдачи правильных кодов HTTP). Кое-что я вообще с наскока реализовать не смог — например, выдачу HTML-версий страниц для Яндекса (их пауки в 2020-м году не умеют индексировать сайты на JavaScript).

В общем, опыт вышел полезным, но результат — настолько спорным, что в конце концов я бросил эту затею, а получившегося Франкенштейна выложил на GitHub. Возможно, он кому-то сэкономит время на решение задач в духе «как вывести через Vue.js произвольный HTML» или «как научить VueI18n работать с русским языком».

Документации там нет, правда. Я сначала хотел подробно расписать, как что работает, но быстро понял, что потрачу уйму времени без видимой пользы. Вероятно, буду возвращаться к этой теме под настроение — а пока, так сказать, ограничимся парадигмой MVP :-)

21 июля 2020 блог вебдев Node.js готово JavaScript

Сломанный велосипед

1C:Drive

Думаю, эту картинку для 1Ci нарисовал человек, прямо вот максимально далекий от разработки самой конфигурации и программирования вообще. Потому как программист видит на ней ровно два месседжа о продукте:

  1. Это велосипед.
  2. Он сломан.

20 июля 2020

Запускатор служб

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

Результат можно посмотреть на GitHub'е. No big deal — хотел поупражняться в языке и упростить ежедневную рутину: в моей системе наберется десятка полтора прожорливых сервисов, которые нужны для работы, но бесполезны в другое время. Вручную останавливать, а потом запускать этот зоопарк неудобно, а вот одной командой — совсем другое дело!

13 июля 2020 готово PowerShell рабочее место

Размер данных базы 1С

На прошлой неделе листал комментарии к 8.3.15 и наткнулся на метод ПолучитьРазмерДанныхБазыДанных(). Стало любопытно, как эта штука работает и насколько её данные расходятся с теми, которые можно получить из, например, Management Studio.

В итоге накатал что-то вроде консоли, через которую методу можно передавать разные метаданные, и принялся следить за запросами платформы к БД.

В общем, размер данных платформа считает примерно таким выражением:

CAST(
    SUM(
        CAST(
            DATALENGTH(T1._Fld40) AS NUMERIC(12, 0)
        )
    ) AS NUMERIC(18, 0)
)

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

Выводы?

Ну, во-первых, понятно, почему у метода такое дурацкое название. Он считает не размер таблиц, как я изначально подумал, а именно размер данных — то есть на оценку не влияют ни расходы на схему данных, ни расходы на индексы, ни механика экстентов. Учитывается только размер самих данных, которые хранятся непосредственно в объекте.

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

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

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

В общем, на работающем проде применять с осторожностью.

11 июля 2020 готово

Выгрузка стандартных обработок

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

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

КопироватьФайл(
    "v8res://mngbase/StandardDataChangeHistory.epf",
    "Q:/StandardDataChangeHistory.epf"
);

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

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

5 июля 2020 работа

Тонкое искусство приземления

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

Well, technically, it did land… Just not in one piece.

:D

В комментариях не отстают:

Space X : Launches astronauts for the first time.

Youtube: Let's recommend this video!

31 мая 2020 тем временем

Фред

Игорь Сахаров перепел «Фреда» Короля и Шута на английском языке:

Роцк! Ро-о-оцк! :-)

Если серьёзно, я уже как-то писал про это, но мне ни разу не лень повторить: такие штуки — один из самых простых и приятных способов запомнить кучу слов и оборотов.

Да, вокруг хватает крутанов с цепкими мозгами, которым этот трюк не нужен; он, скорее, для обычных ребят вроде меня самого. У нас все эти what a good match you and I и spellbound by your beauty pure and true в других обстоятельствах порой улетучиваются из головы, ну… Секунд через десять?

А так — смотришь ролик, невольно подпеваешь и не замечаешь, как текст намертво оседает в памяти.

24 мая 2020 английский

Об идеальном балансе

Хочу поделиться парой классных текстов о современном софте. Они не особенно свежие (первый-то уж точно), но наверняка ведь кто-то пропустил:

К чему я это вспомнил? На прошлой неделе участвовал в хакатоне среди программистов нашей компании. Узнал кучу клевых штук, а по итогам даже занял первое место (вместе с ещё двумя участниками, у которых получились очень похожие решения).

(если вы — тоже сотрудник «Первого БИТа», то итоги хакатона лежат на портале; там же — отзыв другого победителя, Димы Лещенко)

В процессе нужно было развернуть и настроить целую гору софта: EDT, RabbitMQ, Docker, GitLab, JIRA, SonarQube и ещё вагон инструментов поменьше и попроще. Ладно, к RabbitMQ у меня претензий нет: легкий и быстрый, но вот остальное… Про хороший аппетит EDT я знал и раньше, а вот прожорливость GitLab и JIRA по-настоящему удивила.

Да, в моём случае всё запускалось в докере; да, конфигурация не была оптимальной (например, было развернуто несколько серверов PostgreSQL вместо одного); да, докер был для Windows, а его реализация под эту платформу — тема для едких шуток у всех сисадминов, с которыми я знаком. Но потратить 12 гигабайт ОЗУ прямо со старта?! Про процессор вообще молчу — нагрузка была такая, будто компьютер просчитывал ядерный взрыв в реальном времени.

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

У меня нет ответа. Думаю, ни у кого нет. Это такой вечный холивар внутри профессии: какую сторону не займи, на деле все равно балансируешь между двумя крайностями — с одной стороны, надо писать хороший, быстрый код, с другой — надо не свалиться в преждевременную оптимизацию. Первое в итоге дает чистое удовольствие от хорошо сделанной работы: вспоминаешь, что ты, черт побери, неплох! Второе — просто позволяет не нажить бед с башкой в поисках идеального алгоритма сортировки пузырьком :-)

20 мая 2020 работа

Просмотр таблицы значений

Закинул на GitHub короткую функцию, с помощью которой произвольную таблицу значений можно превратить в табличный документ.

Для чего? Чтобы посмотреть её содержимое. Саму таблицу на клиент передать нельзя, а вот табличный документ на её основе — можно. Кроме того, у табличного документа есть клиентский метод Show(), который отображает его в отдельном окне.

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

16 мая 2020 готово

Самодокументируемый код

Всем привет! С вами снова Джонни Кэтсвилл и передача «Самодокументируемый код»!

Сегодня мы переберем строки таблицы значений с Н по Й:

:|

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

Дело даже не в том, что этот участок труднее разобрать — просто он неплохо говорит о качестве модуля в целом. Скорее всего, писавший его программист был вымотан, и поблизости можно встретить что-нибудь вроде отважной выгрузки справочника номенклатуры в таблицу значений, чтения кучи объектов «через точку» и прочего тяп-ляп.

3 мая 2020 код с запашком

Управление службами через PowerShell

На днях набросал себе несколько скриптов на PowerShell'е для запуска и остановки служб на рабочем ПК. Наработки в итоге не пригодились, так что зафиксирую себе пару примеров на будущее, чтобы не потерять:

get-service -Name 1C:Enterprise* | Where-Object {$_.status -eq 'running'} | stop-service -pass
get-service -Name *SQL* | Where-Object {$_.status -eq 'running'} | stop-service -pass

Обе строки выше делают одно и то же: ищут службы с определенным именем, проверяют их состояния и, если службы запущены — останавливают их. Имена служб удобно задавать через шаблоны — например, у Microsoft SQL Server целый выводок служб с разным назначением и их удобно пристрелить одной строкой. Или, скажем, служба сервера 1С:Предприятия — она одна, зато её длиннющее название просто лень писать полностью :-)

Примерно тот же подход, кстати, работает и с обычными процессами. Так, строка ниже ищет процесс obs64; если находит — останавливает.

get-process -Name obs64 | stop-process -pass

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

28 апреля 2020 PowerShell рабочее место

Ранее Ctrl + ↓