Показаны сообщения с ярлыком vbscript. Показать все сообщения
Показаны сообщения с ярлыком vbscript. Показать все сообщения

28 апр. 2021 г.

Охота на утечки памяти

В отладочную версию gedemin.exe и новейшую версию класса TCreator добавлено расширенное логирование:

  1. Транзакции. Логируется старт и завершение транзакции. Показывается количествоактивных транзакций в данный момент времени. 
  2. IBSQL. Логируется выполнение запроса. Для SELECT запросов логируется закрытие. Показывается количество открытых на чтение запросов.
  3. Designer. Логируется создание и удаление объекта. Показывается количество объектов в памяти.
  4. TCreator. Логируется создание и удаление экземпляра класса. Показывается количество TCreator  в памяти.

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

27 апр. 2021 г.

NB! Об использовании TCreator и других VB классов

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

Особенности классов VBScript

  1. Событие Terminate вызывается только в процессе удаления из памяти последнего экземпляра данного класса.
  2. Наличие кольцевых ссылок, в т.ч. цепочек кольцевых зависимостей любой длины, приведет к тому, что ни один из экземпляров не будет удален сборщиком мусора и останется в памяти до конца работы программы.

Как мы столкнулись с проблемой неудаления объектов из памяти

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

Возникает вопрос, как бороться с вышеуказанной частной ситуацией и как правильно работать с классами VBScript, чтобы максимально обезопасить себя от утечек памяти?

Использовать Designer.CreateObject -- Designer.DestroyObject

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

Создать копию класса TCreator

Вы автор подсистемы на платформе Гедымин. Код протестирован на корректную работу с ресурсами. Но, как обезопасить себя от ситуации, когда другая подсистема создаст долгоживущий TCreator и заблокирует все ваши механизмы очистки памяти? 

Следует создать полную копию класса TCreator в рамках своей подсистемы и использовать ее для выделения ресурсов.

А надо ли каждый раз выделять-уничтожать ресуры?

В указанном выше приложении сотни раз за рабочую смену выделялись и уничтожались одни и те же ресурсы -- транзакции и запросы к базе данных. Одно из решений -- пул ресурсов. Нужные объекты создаются единожды и привязываются к окну (у нас есть соответствующее свойство Objects у формы). Остается только стартовать/комитить транзакции в нужных местах и выполнять/закрывать запросы к базе данных, без уничтожения самих объектов. 

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

Принудительно уничтожить ресурсы в TCreator

В крайнем случае, ресуры выделенные через TCreator можно уничтожить принудительно, вызвав метод DestroyAllObjects. Теперь, даже если сборщик мусора не сможет удалить экземпляр TCreator, то в памяти останется только он, но не связанные с ним ресурсы.

Уничтожать TCreator как можно раньше

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

...
Dim Creator
Set Creator = New TCreator
...
здесь работаем с ресурсами
...
больше нам выделенные ресурсы не нужны
Set Creator = Nothing
...
продолжается код функции/процедуры
...

22 апр. 2021 г.

Новый сервер компиляции gedemin.exe

Многие годы за автоматическую компиляцию gedemin.exe и формирование пакетов прикладных решений отвечали несколько изощренных bat файлов. Технология пакетной обработки была заложена вместе с первыми версиями DOS еще в начале восьмидесятых прошлого века и предоставляет разработчику очень скудный функционал. Для любого отступления за рамки элементарного копирования/удаления и вызова сторонних программ приходится создавать свои утилиты или приспосабливать свободно доступные из всемирной сети.

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

Все бы ничего, пока строго один gedemin.exe "выпекался" из текущих исходников ветки master в репозитории. Несколько лет назад на крупных предприятиях значение генератора GD_G_UNIQUE стало подбираться к верхнему пределу доступных положительных 32-х битных целочисленных значений. В качестве быстрого решения проблемы мы ввели механизм поиска доступных интервалов в последовательности идентификаторов во всех таблицах базы и записи их в специальную таблицу, откуда Гедымин берет новые идентификаторы по мере необходимости. И... начали думать над долговременным решением, которым должно стать использование 64-х битных целочисленных идентификаторов записей.

Надо сказать, что 20 лет назад никто не задумывался над проблемой исчерпания идентификаторов и внутри исходного кода разработчики сплошь и рядом использовали тип Integer или полагались на длину строго в 4 байта при записи и чтении из буфера. Что еще хуже, Delphi 5 не поддерживает тип Int64 в библиотеке типов COM, через которую объекты Гедымина взаимодействуют с программным кодом на VBScript.

Первым шагом трансформации исходного кода стало введение специального типа для идентификаторов TID и набора функций для преобразования идентификаторов в/из строк, целых чисел, чисел с плавающей точкой и т.п. Разумеется, что замена типа затронула большинство из файлов проекта. Для справки, всего Гедымин насчитывает 5164 .pas файла, если считать вместе со сторонними библиотеками. В зависимости от символа условной компиляции ID64 тип TID превращается либо в 32-х битный Integer, либо в Int64. 

Существующий файл базы данных должен пройти через процедуру конвертации, которая меняет домены dintkey, dforeignkey и выполняет последовательность бэкап-рестор. Процесс конвертации базы данных необратим. После перехода на 64 бита вернуться назад уже нельзя.

Таким образом, у нас появляются три версии выполняемого файла:

  1. стабильный, проверенный временем gedemin.exe c 32-х битными идентификаторами.
  2. gedemin.exe на основе новых исходников с 32-х битными идентификаторами.
  3. gedemin.exe c 64-х битными идентификаторами.

Поддерживать такое хозяйство с помощью устаревшей технологии bat файлов и остаться при этом в здравом рассудке не представляется возможным. Пару месяцев назад мы начали проект gbuilder -- сервер компиляции Гедымина и прикладных решений. В настоящее время готова первая очередь. Из специальной ветки с именем india компилируется gedemin.exe на основе стабильных исходников. Прикладные решения формируются из ветки master репозитория gedemin-apps. Сервер компиляции предоставляет разработчику интерфейс через чат-бот в телеграме @gbuilderbot. Компиляция активируется через github webhooks по мере поступления на сервер изменений в исходный код.

В скором времени будет добавлена компиляция бета версий gedemin.exe из новейших исходников с 32-х битными и 64-х битными идентификаторами.

gbuilder реализован на платформе NodeJS. Язык программирования Typescript. Скомпилированные файлы можно скачать из соответствующего раздела нашего сайта.


2 авг. 2016 г.

GS.MulDiv

Для правильного округления введена функция MulDiv, которая доступна из макросов и из FastReport4. Теперь, вместо кода:

'По реализации по розн. ценам (сумма НДС, гр9 )
S95 = round(acc_CalcTaxPosition("148725640_116707345", GS.NG(BeginDate), EndDate) / 1000 + 1/100000, 3)

пишем:

'По реализации по розн. ценам (сумма НДС, гр9 )
S95 = GS.MulDiv(acc_CalcTaxPosition("148725640_116707345", GS.NG(BeginDate), EndDate), 1, 1000, 1, 3)

14 мая 2016 г.

True UTF-8 encoding

XML файл в кодировке UTF-8 можно сформировать используя только ASCII и кодируя все национальные символы через &#x0 последовательности. Дешево и сердито. Например, такой подход предложен здесь.

Но, если мы хотим, чтобы XML файл красиво отображался в текстовом редакторе, системе контроля версий и т.п., надо сохранить его в настоящей кодировке UTF-8. Для этого мы добавили в платформу функции WIN1251ToUTF8 и UTF8ToWIN1251, которые принимают на вход два потока и перекодируют данные между ними. В дополнение к ним идут функции ReplaceXMLTags и ExpandXMLTags.

Пример использования см. здесь.

13 дек. 2015 г.

Выполнение скрипт-функции по расписанию

Как организовать выполнение скрипт-функции по расписанию?

Автозадачи решают данную проблему, но требуют постоянно загруженного gedemin.exe и открытого коннекта к базе данных. Начиная с версии 2.9.2 можно использовать планировщик операционной системы и два новых параметра командной строки:

gedemin.exe /run 147000555_256456378

Выполняет скрипт-функцию с заданным РУИДом после загрузки платформы, инициализации подсистемы автозадач и выполнения всех автозадач, назначенных на старт системы (если таковые имеются).

gedemin.exe /run 147000555_256456378 /exit

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

gedemin.exe /exit

Завершение выполнения gedemin.exe сразу после загрузки, подключения к базе данных, инициализации подсистемы автозадач и выполнения всех автозадач, назначенных на старт системы.

Указанные параметры могут использоваться вместе с параметрами подключения к базе данных.

23 окт. 2014 г.

Код добавления нулей слева к числу:

S = Right("00000000000" & V, 12)

на 5% быстрее чем:

S = String(12 - Len(V), "0") & V

Очевидно из-за меньшего количества вызова функций.

29 апр. 2014 г.

13 мар. 2011 г.

Вызов функции по ссылке в VBScript

Хотя в языке VBScript отсутствует понятие указатель на процедуру/функцию (собственно, отсутствует понятие указатель как таковое), достигнуть похожего эффекта можно с помощью конструкции Eval. В приведенном ниже примере на вход процедуры Test передается имя функции, которая должна быть вызвана.
Option Explicit

Function TestAdd(A, B)
  TestAdd = A + B
End Function

Function TestSubstract(A, B)
  TestSubstract = A - B
End Function

Sub Test(FuncName, K, N)
  MsgBox Eval(FuncName & "(K, N)")
End Sub

Sub Macros
  Test "TestAdd", 100, 40        ' на экране 140
  Test "TestSubstract", 100, 40  ' на экране 60
End Sub