Заметки

Позднее Ctrl + ↑

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

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 рабочее место

Black Mesa

Легендарный, но уже откровенно постаревший Half-Life тщательно отретушировали и дополнили кучей новых задумок. Вышло здорово; прям видно, что фанаты старались :-) Особенно Зену пошло на пользу — разве что завод, по-моему, стоило сделать покороче. Впрочем, бой с Нихилантом переделали так, что он с лихвой окупает все страдания.

Есть мелкие косяки с музыкой, оптимизацией и переводом, но ничего особо критичного. Учитывая, сколько шла разработка — удивительно, что вообще дошло до релиза сквозь типичные для долгих проектов проблемы вроде усталости и выгорания команды. И это не считая титанической работы по переделке проекта двадцатилетней давности на современные рельсы! Quite a nasty piece of work you managed over there, guys. I am impressed.

Болото Кристаллическая пещера Пылающее небо Мухоловка

13 апреля 2020 видеоигры

Пустая() или ЗначениеЗаполнено()?

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

СсылкаЗаполнена = Ссылка <> Неопределено И Не Ссылка.Пустая();

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

На моей памяти ни тот, ни другой метод узким местом никогда не были, но меня разобрало любопытство; написал тест, который проверяет ссылочную переменную на пустое значение обеими способами и замеряет время, которое было потрачено.

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

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

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

11 апреля 2020 готово

Запись видео в ShareX

Прежде чем приспособить OBS Studio для записи видео (писал о ней в предыдущей заметке), я пробовал использовать ShareX. Программа поначалу неплохо справлялась, однако от неё пришлось отказаться из-за неприятного бага: иногда при попытке завершить запись софт портил полученное видео.

Я гуглил, но причину понять так и не смог. Впрочем, неудивительно — ShareX использует ffmpeg для записи видео, а это дитя опенсорца вместо адекватной диагностики невозмутимо вываливает целый рулон логов. Разбирать его без соответствующего опыта — занятие для клинического оптимиста.

Впрочем, как инструмент для создания скриншотов ShareX почти безупречна. Обилие опций слегка напоминает замотанного человека в приступе гиперактивности :-)

Настраиваем скриншоты

— Протестирую сам! Сам все протестирую! И полы вымою!

Эффективность не работает. Илья Якямсев

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

Запись видео

Я работаю удалённо и с коллегами общаюсь через Zoom и Slack. В основном в чатах, но многие темы по-прежнему проще обсудить голосом. И вот тут-то возникают проблемы: приходится забыть про индексацию и поиск по сообщениям, а главное — нет никакого архива разговоров. Иногда очень нужно вспомнить: кто говорил? Что конкретно? Когда?

Zoom и Slack сами по себе эту проблему не решают. Первый умеет записывать только те конференции, которые организуешь ты или которые тебе разрешили писать (короче, далеко не все), а второй писать видео не умеет вообще.

Решение проблемы

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

Настройка OBS Studio

В настройках нужно указать папку, куда будут складываться записанные видео (Вывод → Запись → Путь записи). Поскольку мы пишем звонки и файлов будет много, лучше сразу разложить их в папки по дням — для этого идем в расширенные настройки и в поле «Формат имени файла» указываем нечто вроде %CCYY-%MM-%DD/%hh-%mm-%ss.

Быстрое управление записью

Конечно, лезть в OBS при каждом звонке неудобно, но у неё есть куча хоткеев — можно прожимать какие-нибудь заковыристые комбинации в начале и в конце разговора. Однако со мной это не сработало — придуманные хоткеи почему-то постоянно вылетали из головы. Даже стикеры не помогали.

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

С оборудованием всё было просто: у меня эргономичная клавиатура от Microsoft, программируемых кнопок на ней — вагон. Хуже с софтом: OBS Studio понимает некоторые параметры запуска, но список откровенно бедноват — даже остановки записи нет. Хотелось бы побольше.

В итоге решил задачу через плагин к OBS Studio, который дает порулить запущенным экземпляром программы. Он придуман для удаленного управления записью (например, со смартфона), но никто не мешает использовать его локально.

Схема получилась такая:

  1. Ставим OBS Studio и плагин obs-websocket к ней. Снимаем галку «Enable System Tray Alerts» в настройках (Tools → WebSockets Server Settings), не то программа примется сообщать о каждом чихе.
  2. Кладем куда-нибудь утилиту, с помощью которой этим плагином можно рулить из командной строки.
  3. Вешаем на одну кнопку OBSCommand.exe /startrecording, а на другую — OBSCommand.exe /stoprecording.
  4. Готово, вы великолепны!

При вызове на долю секунду видно консоль (OBSCommand запускается, связывается с obs-websocket и ждет ответа). Сначала хотел как-нибудь скрыть это окно, но, поразмыслив, передумал — если OBS Studio не будет запущен или возникнет какая-то проблема, консоль не закроется и я хотя бы узнаю, что запись не началась.

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

Неразрешимые ссылки

Чаще всего неразрешимые ссылки на объекты метаданных образуются в ходе удаления объектов (и, иногда, при объединении конфигураций). На ИТС есть статья по этой теме, там хорошо описаны детали.

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

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

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

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

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

10 марта 2020

Фильтрация стандартных реквизитов

Короткий фрагмент кода из обработки для настройки механизма истории данных, о которой я только что писал. Эта функция определяет, является ли Attribute стандартным реквизитом с именем StandardAttributeName, принадлежащим объекту метаданных MetadataObject. Где она нужна? Допустим, вы перебираете стандартные реквизиты объекта и по какой-то причине хотите пропустить один из них.

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

If Attribute = MetadataObject.StandardAttributes.Order Then

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

О'кей, скажете вы — может, тогда не будем усложнять и напишем вот так?

If Attribute.Name = "Order" Then

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

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

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

Что до конструкции Try/Catch — она тут на тот случай, если стандартного реквизита с таким именем в конфигурации нет вообще (и попытка обратиться к нему по имени приведет к ошибке).

9 марта 2020 готово

Настройки истории данных

Выложил на GitHub обработку для настройки механизма истории данных, который сравнительно недавно появился в платформе. Эта обработка:

  1. Строит дерево объектов, для которых может вестись история данных;
  2. Подсказывает, для каких объектов история данных ведется сейчас;
  3. Дает возможность включить или выключить историю данных для объектов и их реквизитов.

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

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

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

9 марта 2020 готово работа

Девятый вал

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

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

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

9 марта 2020 тем временем работа

Топ исключений по ТЖ

Выложил на GitHub скрипт на баше, который по собранному технологическому журналу 1С определяет наиболее частотные исключения.

Скрипт анализирует события EXCP. Это основной источник информации об ошибках, хотя, конечно, не единственный: например, есть EXCPCNTX (событие, которое началось, но не закончилось в тот момент, когда произошла ошибка). Кроме того, если исключение происходит во время серверного вызова, то в поле RetExcp у события CALL будет текст ошибки, которая вернется на клиент.

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

16 февраля 2020 bash готово

Отладка через HTTP

В конце недели столкнулся с довольно глупой ситуацией: отладка через TCP/IP на сервере 1С перестала работать. То есть сам по себе режим отладки включен, однако конфигуратор «не видит» контекстов отладки на сервере.

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

Решил проблему, переключившись на отладку через HTTP. Инфрастуктурно это более сложная схема, но настраивается она довольно просто; если вкратце, то нужно:

  • Запустить службу агента сервера с ключами «-debug -http».
  • В параметрах отладки конфигуратором (Сервис → Параметры → Отладка) указать HTTP как протокол отладки и сервер отладки кластера как сервер отладки.

Подробнее процесс настройки описан на ИТС.

26 января 2020

Ранее Ctrl + ↓