Приближается 27 января! В этом году дата не слишком круглая, но от этого
не менее внушительная. Golden Software исполняется 29 лет!
Мы приглашаем всех друзей на традиционный уже боулинг в клубе Мэдисон в
Минске. В четверг 26 января, в 19:00.
Приходите! Будем рады вас видеть.
23 янв. 2023 г.
Приглашаем всех друзей!
31 дек. 2022 г.
7 окт. 2022 г.
Как меня подвела народная мудрость
Все мы знаем главное программистское правило: если что-то работает -- не трогай, пусть работает. Вот и я, когда переносил нашего зарплатного бота с Windows сервера на облачный VPS думал также. Полностью сархивировал папку в одном месте и один-в-один запустил в другом. Всё. Дело сделано.
Сегодня утром меня поднял срочный email от облачного провайдера. Так и так, мол, мы прикрываем вашу лавочку, потому как вы не пользователь, а какой-то интернет террорист, который атакует с наших серверов пол Европы. Следом пришло автоматическое уведомление, что хотя они и выделяют нам канал на 400 MBit/sec это не значит, что мы должны использовать прямо так 400 Mbit все время.
В итоге VPS заблокирован. Доступа к нему нет. Хелп деск провайдера вежливый, но находится совсем отдельно от дата центра и технического персонала. Рекомендация звонить напрямую в дата центр звучит как писать на деревню дедушке.
Хорошо, что перенесенный сервер работал в тестовом режиме и у меня была исходная копия. Пришлось полностью снести VPS, переустановить с нуля Ubuntu и начать все с начала.
Что произошло?
Меня подвело следование главному программистскому принципу (см. выше). Я долго не обращал внимания, на то, что гитхаб шлет мне уведомления, что в библиотеках, которые использует наш софт, найдены критические уязвимости, позволяющие злоумышленнику получить контроль над машиной. Подвела и долгая жизнь на Windows серверах, которые в силу своей меньшей популярности вызывают и гораздо меньший интерес у хакеров. Очевидно, кто-то завладел нашей машиной через одну из дыр в сторонней библиотеке и начал атаковать другие сервера или спамить (а мог бы криптовалюту майнить за наш счет).
Вывод:
1. Всегда обновляйте библиотеки. Выделите на это регулярный день, например, утро понедельника.
2. Если проект стабильный, уже не развивается, тем не менее следите, что пишет гитхаб по поводу найденных в нем уязвимостей. И, если найдены с пометкой Critical или High -- сразу же обновляйте код.
3. Облачный сервер сегодня есть -- завтра его нет. И ни какие мольбы не помогут вернуть вам утраченные данные. Работать в облаке можно только при настроенной системе автоматического 100%-ного бэкапа с переносом данных на другой физический сервер, обязательно к другому провайдеру.
30 сент. 2022 г.
Настройка принтера Poscenter RP-100 для печати кириллицы
Мы столкнулись с проблемой вывода кириллических символов на принтер Poscenter RP-100 при печати чеков из Гедымина через программную кассу TitanPOS.
Для корректной настройки необходимо в файле конфигурации TitanPOS.conf прописать такие параметры:
...
"EscPosSubstituteCharset1251": 17,
"DefaultCharsetName": "IBM 866",
...
14 авг. 2022 г.
Как приступить к разработке платформы Гедымин
Для разработки платформы необходим компьютер с операционной системой Windows не ниже Windows XP и 8 или 16 Гб ОЗУ. Мы рекомендуем больше памяти при использовании виртуальной машины.
Существует два варианта подготовки инструментария для компиляции. Первый -- установить Delphi 5 и Firebird 2.5 непосредственно на компьютер следуя этой инструкции.
Второй вариант -- это обратиться к специалистам нашей компании за готовой виртуальной машиной с предустановленной системой Delphi и сервером баз данных.
Существует два режима работы Гедымина с сервером базы данных. В режим клиент-сервер необходима клиентская библиотека (fbclient.dll или gds32.dll -- обязательно разрядностью 32 бита!) и запущенный сервер (на этом же компьютере или доступный по сети).
В режиме встроенного сервера необходима библиотека fbembed.dll которая, собственно, представляет собой весь сервер Firebird выполняющийся в адресном пространстве gedemin.exe.
В настоящее время Гедымин поддерживает серверы Firebird 2.5, Firebird 3 и Firebird 4. Для двух последних необходимо дополнительно настроить файл конфигурации firebird.conf.
Некоторые базы данных могут использовать внешние функции из библиотеки GUDF.DLL. Ее следует скачать с нашего сайта и поместить в подкаталог UDF сервера.
Для запуска Гедымина в рабочем режиме потребуется файл базы данных. Это может быть т.н. эталон -- чистая база данных без загруженных прикладных решений. Свежий эталон всегда находится здесь. Либо, можно воспользоваться одной из установок прикладных решений и взять из нее файл базы данных уже с загруженными пространствами имен (так у нас называются файлы с исходным кодом прикладных решений).
Далее, переходим к получению исходного кода, который находится на github.com в приватном репозитории. Обратитесь к сотрудникам компании, чтобы они предоставили вам доступ к нему.
Мы работаем по такому алгоритму:
0. Настраиваем git. Прописываем свое имя пользователя и email командами:
git config --global user.name "FIRST_NAME LAST_NAME"
git config --global user.email "MY_NAME@example.com"
1. Создаем папку \Golden, переходим в нее и клонируем репозиторий себе на компьютер командой:
git clone https://github.com/GoldenSoftwareLtd/gedemin-private.git gedemin
Выполняем действия для подготовки исходников к компиляции, которые указаны в инструкции выше. Компилируем все библиотеки и сам Гедымин согласно инструкции.
2. Основная ветка -- master -- сразу идет в продакшн. В эту ветку мы не комитим изменения напрямую. Перед тем как делать какие-то изменения снимаем код с сервера и создаем новую ветку:
git pull
git checkout -b IssueXXXX
Команду выполняем в папке Gedemin. Имя ветки на ваш выбор.
3. Делаем изменения. Затем комитим их в свою ветку:
Важно! Перед комитом проверяем, что исходники компилируются в Delphi без ошибок.
git commit -a -m "Пишем комментарий"
Перед комитом можно просмотреть сделанные изменения командой:
git status -uno
Важно! Не следует включать в комит следующие файлы, даже если они были изменены:
- gedemin.res, и, вообще, все *.res файлы
- файлы экранных форм *.DFM, где только поменялись координаты экранных элементов при открытии формы в Delphi
Если такие файлы показывает git как измененные, следует вернуть их в исходное состояние. Например, в окне комита оболочки TortoiseGit выбрать такой файл в списке и из контекстного меню вызвать команду Revert.
4. Загружаем изменения в свою ветку в github:
Первый раз следует выполнить команду для загрузки на сервер вашей ветки:
git push --set-upstream origin <YOUR BRANCH NAME>
git push
5. Идем на сайт github.com и создаем Pull Request из вашей ветки в мастер. После того, как ведущий разработчик подтвердит ваши изменения, они попадут в мастер и пойдут в продакшн.
6. На своем компьютере переключаетесь на ветку мастер и снимаете последние изменения:
git checkout master
git pull
7. Подключив телеграм бот @gbuilderbot можно получать уведомления об автоматической компиляции проекта и сборке дистрибутивов.
С чего начать?
Мы планируем доработки и регистрируем ошибки и пожелания в списке Issues в этом репозитории.
Проконсультируйтесь в ведущим разработчиком и выберите себе для начала что-нибудь простое, чтобы познакомиться с организацией исходного кода и внутренней структурой платформы.
Например, доработки вроде этой вообще не требует написания кода, а только правки экранных форм.
По мере ознакомления и вникания в нюансы внутренней организации платформы можно переходить к более сложным заданиям.
8 авг. 2022 г.
Вакансия: разработчик на Delphi со знанием SQL
Требуется программист для развития технологической платформы Gedemin и экономических задач на ее базе. Хорошее знание Delphi, WinAPI и реляционных баз данных обязательно.
Мы используем Git для контроля версий. Общее представление об экономике, бухучете так же приветствуется, но отсутствие глубоких знаний в данных областях не будет препятствием. Главное желание постоянно развиваться и учиться.
Командировки маловероятны, но изредка могут быть. В основном, это однодневные выезды по Беларуси.
Гибкий график. Возможна удаленная работа.
Присылайте свои резюме на адрес: job[at]gsbelarus.com
15 июн. 2022 г.
How to find useless foreign keys in a database
With times, old databases tend to overgrow with unnecessary or just empty fields. Whilst regular fields are not much of a concern, those with foreign key constraints, especially in tables with millions of records, would needlessly inflate the database file and put performance penalty on every insert/update/delete operation, as appropriate index need to be updated.
For example, an index on a field that holds nothing more than NULL values over a table with 100 000 000 records has a size approximately of 600 MB.
The query below helps to spot fields with foreign keys, which contain no more than a given count of unique values (by default, the variable maxuniqvalues is set to 1 to find all fields which are empty or contain exactly one value). Additionally, the condition on minimal record count in a table in question could be set through variable minreccnt.
The result data set includes the following columns: name of the relation, number of records in the relation, name of the field, value (only the first value is shown), and a list of objects dependent on the field. The latter will help a lot if the field to be deleted later.
EXECUTE BLOCK
RETURNS(
rn VARCHAR(31),
reccnt INTEGER,
fkfieldname VARCHAR(31),
val INTEGER,
dependent VARCHAR(8192)
)
AS
DECLARE VARIABLE minreccnt DOUBLE PRECISION = 1000000;
DECLARE VARIABLE maxuniqvalues DOUBLE PRECISION = 1;
BEGIN
FOR
SELECT
rc.rdb$relation_name,
CAST((1 / idx.rdb$statistics) AS INTEGER),
idxsfk.rdb$field_name,
(SELECT
LIST(TRIM(d.rdb$dependent_name))
FROM rdb$dependencies d
WHERE
d.rdb$depended_on_name = rc.rdb$relation_name
AND
d.rdb$field_name = idxsfk.rdb$field_name)
FROM
rdb$relation_constraints rc
JOIN rdb$indices idx
ON idx.rdb$index_name = rc.rdb$index_name
JOIN rdb$index_segments idxs
ON idxs.rdb$index_name = idx.rdb$index_name
AND idxs.rdb$field_position = 0
JOIN rdb$relation_constraints rcfk
ON rcfk.rdb$constraint_type = 'FOREIGN KEY'
AND rcfk.rdb$relation_name = rc.rdb$relation_name
JOIN rdb$indices idxfk
ON idxfk.rdb$index_name = rcfk.rdb$index_name
JOIN rdb$index_segments idxsfk
ON idxsfk.rdb$index_name = idxfk.rdb$index_name
AND idxsfk.rdb$field_position = 0
WHERE
rc.rdb$constraint_type = 'PRIMARY KEY'
AND
(idx.rdb$statistics > 0
AND idx.rdb$statistics < (1.0 / :minreccnt))
AND
idxfk.rdb$statistics >= (1.0 / :maxuniqvalues)
ORDER BY
idx.rdb$statistics ASC
INTO
:rn, :reccnt, :fkfieldname, :dependent
DO BEGIN
EXECUTE STATEMENT
'SELECT FIRST 1 ' ||
:fkfieldname ||
' FROM ' ||
:rn
INTO :val;
SUSPEND;
END
END
13 февр. 2022 г.
Текущее состояние gdmn-nxt
-
Автоматически создавать на сервере энд поинты для CRUD операций над бизнес-объектами.
Автоматически создавать на клиенте UI для работы с данными (форма просмотра и диалоговое окно в терминах Гедымина).
28 янв. 2022 г.
Зависание nodejs при запуске через окно терминала Windows
-
Отключить режим Quick edit в окне Properties терминала (см. скриншот ниже)
Использовать другой терминал (например, git bash)
Запускать nodejs через Task Scheduler операционной системы.
26 янв. 2022 г.
Шрифт MS Sans Serif для кирилической кодировки Win-1251
20 янв. 2022 г.
Ровно в следующий четверг, 27 января, нам исполняется 28 лет! Традиционный боулинг, как обычно, в 19:00 в клубе Мэдисон. В ЧЕТВЕРГ! Будем рады видеть всех наших сотрудников! Как действующих, так и бывших.
🎳
Различие в поведении finally между Delphi и Javascript
Пришлось потратить пару дней в поисках трудновыявимой ошибки. Все дело в том, что finally работает в Delphi и NodeJS по-разному. Будьте осторожны.
Например, в Делфи, код в секции finally выполнится ПЕРЕД окончанием выполнения функции:
В Javascript, код в секции finally (даже не смотря на наличие await) выполнится уже ПОСЛЕ того как результат функции будет передан получателю.
Теперь представим такую ситуацию. На веб сервер посылается сначала запрос на изменение данных, а после его выполнения запрос на получение (перечитывание) данных. Если мы будем делать commit транзакции в секции finally, то возможна такая ситуация, когда первый запрос уже завершится и вернет результат на клиента, который, в свою очередь, выполнит второй запрос к серверу, а транзакция все еще будет не подтверждена. В этом случае клиент получит старые данные, не соответствующие тому, что находится в базе данных.
Выход в данном случае или комитить транзакцию до секции finally, или использовать мьютекс на несколько обработчиков, который не даст прочитать данные, пока не запишется транзакция, которая меняла эти данные.

