14 мар. 2018 г.

Использование сторонних библиотек

  • Если библиотека активно развивается и широко используется (например, React), ставим ее через npm/yarn. Периодически обновляем до последней версии и тестируем на совместимость с существующим кодом.
  • Библиотека широко используется, но уже не развивается (например, classnames), действуем аналогично предыдущему пункту.
  • Автор забросил библиотеку, но она активно развивается в одном из форков (найти такой мы можем с помощью Network diagram на сайте github. Пример: jison). Устанавливаем библиотеку из гит репозитория активного форка с помощью npm. Если промежуточные изменения нестабильны -- устанавливаем стабильную ветвь или определенный коммит. Дополнительно, клонируем себе репозиторий в папку Lib и периодически делаем pull.
  • Мы собираемся развивать стороннюю библиотеку. Делаем ее форк в свой аккаунт. Клонируем в папку Lib и ведем разработку в ней. Устанавливаем в проект с помощью npm из гит репозитория своего форка.
  • Папка Lib не входит в проекты.
  • Не используем git submodule для установки сторонних библиотек внутрь своего проекта. По крайней мере на VS Code наличие сабмодулей загромождает интерфейс панели управления git. Да и удаление сабмодуля из проекта дело хлопотное.

5 мар. 2018 г.

Конкретизируем следующий шаг

  1. Есть разбор предложения "покажи всех клиентов из минска". сейчас он рисует дерево, где слова зеленые, а остальные узлы белые.
  2. Наша задача: найти сопоставления для сущностей и, там где найдено, мы будем закрашивать узлы дерева красным цветом.
  3. Для этого нам понадобится логическая ER модель базы данных.

    Текущая реализация строит ее на основе физической структуры, но это не совсем то, что нам надо. например, в физической модели будет присутствовать таблица GD_CONTACT, хотя такой сущности нет в логической модели, где мы имеем семь сущностей: Папка, Группа, Организация, Банк, Подразделение, Физическое лицо и Сотрудник предприятия.

    Все они базируются на одной физической таблице GD_CONTACT.

  4. Предполагается, что мы добавим в БД (в дополнительные таблицы или в существующую таблицу AT_RELATIONS) информацию, которая позволит нам правильно извлекать логическую структуру БД.
  5. Причем, если такая информация для таблицы не задана, то мы берем ее физическую структуру и на основе нее создаем сущность в логической модели.
  6. Возвращаемся к нашему предложению:

    Сначала мы встречаем существительное "клиентов" -- "клиент" в ед. числе именительного падежа. Через заданный синонимический ряд "клиент - организация" мы можем установить соответствие этого существительного с сущностью "Организация" из логической ER модели базы данных.

  7. Переходим к фразе "из минска". Тут возможны несколько вариантов сопоставления.

    а) Мы наделяем слова в нашем словаре семантическими категориями. Например, Минск -- это город, а город -- это место. Ищем в базе данных и находим справочник "Административно-территориальных единиц", а "Административно-территориальная единица" находится в смысловом ряду с городом, местом.

    б) Мы анализируем предлог "из" в предложной фразе "из Минска" и узнаем, что он имеет смысл места. Далее, через смысловой (синонимический) ряд выходим на справочник "Административно-территориальных единиц".

  8. Весь анализ должен происходить на клиенте. Т.е. надо подумать о передаче схемы с сервера на клиент, представлении ее в памяти и объектах для работы с ней.
  9. На сервере схема должна читаться однократно и храниться в оперативной памяти. Чтобы последующие обращения (покдлючения) клиентов не приводили к повторному длительному чтению всей структуры БД.

25 февр. 2018 г.

вот, чем собственно сейчас и занимаемся.

17 дек. 2017 г.

Каждой программе по своей "проблеме 2000 года"

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

В Гедымине есть своя "проблема  2000 года", а точнее проблема 32-х битного идентификатора бизнес-объекта. Чтобы быть еще более точным, не самого идентификатора а генератора GD_G_UNIQUE, с помощью которого идентификаторы добываются.

Данный генератор стартует со значения 147 000 000 и увеличивается по мере запроса новых идентификаторов. Причем, для сокращения количества запросов к серверу, увеличиение идет с шагом в 100, а неиспользованный на момент завершения программы интервал сохраняется в системном реестре.

Так как у нас ИД объекта -- это знаковое 32-х битное целое, то всего доступно чуть более 2-х миллиардов идентификаторов (мы не учитываем первые 147 миллионов, которые выделены под системные объекты платформы).

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

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

Что делать?

Теоретически есть два варианта решения проблемы. Первый -- это сдвинуть, утрамбовать все идентификаторы "вниз" на выявленные пустые пробелы. Затем изменить значение генератора в соответствии с максимальным ИД в базе.

Технически, для этого придется выполнить следующую последовательность шагов:
  1. Сохранить все существующие ИД в некоторой структуре.
  2. Построить таблицу соответствия старый ИД -- новый ИД.
  3. Отключить все внешние и первичные ключи.
  4. Обновить ВСЕ записи в базе данных, заменяя старые идентификаторы на новые.
  5. После предыдущего шага желательно выполнить бэкап-восстановление БД для чистки мусора.
  6. Восстановить все первичные и внешние ключи.
На базах размером свыше 100 Гб мы не представляем как можно выполнить указанную последовательность в доступное нам технологическое окно (обычно 8-10 часов).  И, если где-то в коде, используется привязка к ИД записи, вместо РУИД, то такой код перестанет работать. К тому же, надо будет как-то вычистить из реестров всех компьютеров сохраненные интервалы или одномоментно заменить все экзешники, скорректировав алгоритм кэширования.

Второй вариант:
  1. Создать таблицу для доступных интервалов идентификаторов GD_AVAILABLE_ID.
  2. При обращении к функции gdcBaseManager.GetNextID проверять не приблизились ли мы к опасной черте. 
  3. Если нет, то работать по-старому -- через генератор. 
  4. Если уже пора, то заполняем таблицу и по-мере необходимости берем очередной интервал из нее.
На практике проверено, что заполнение такой таблицы занимает пару часов даже на самой большой, доступной нам базе данных.

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

15 нояб. 2017 г.

В школе учат, что склонений существительного три, а их на самом деле СЕМЬДЕСЯТ ПЯТЬ!
UPD: 75 только для мужского рода. А всего -- 103! Вот полный список по классификации Зализняка:

20 окт. 2017 г.

К вчерашнему совещанию

Вот уже около 20 лет нам сильно мешает то, что при создании Гедымина мы сразу не решились многие вещи сделать самостоятельно, с нуля. Тогда мы думали: как, менять структуру базы данных? Это слишком сложно, просто не реально. Нам не хватит ни сил, ни знаний. Интерпретатор языка? Нет, слишком серьезная задача. Будем использовать готовый Windows Script Host. Собственный грид для отображения данных? Шутите? Максимум мы подправим тот, что предоставил нам Borland.

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

Поэтому сейчас мы не боимся ни сложности задач, ни объемов работ. Как говорится: дорогу осилит идущий.

JS vs TypeScript vs Flow


Прелесть JS в слабой типизации. Обратите внимание на свойство enabled:
topScores: {
  id: 10050,
  caption: 'Top scores...',
  keyValue: 'F9',
  enabled: () => this.props.gameStage === gameStageEnum.ready,
  checked: false,
  exec: () => {
    const { dispatch } = this.props
    dispatch(showTS())
  }
},
nextPiece: {
  id: 20010,
  caption: 'Next piece',
  keyValue: 'F3',
  enabled: true,
  checked: () => this.props.nextPiece,
  exec: () => {
    const { dispatch } = this.props
    dispatch(nextPiece())
  }
},
Для быстрого прототипирования бесценное качество. Но, попытку изменить что-то в существующем коде отсутствие строгих типов способно превратить в ад из миллиона сложно выявимых ошибок, которые будут проявляться только в момент выполнения и только в определенных обстоятельствах. Как правило, в разгар ключевой презентации у важного клиента.

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

TypeScript это не только контроль типов, но и более-менее приближенная к знакомой всем парадигме реализация ООП.

Winner: TypeScript

Технологический стек

TypeScript -- язык программирования.
Node + Express -- сервер.
Firebird 3 -- база данных. Будем так же смотреть на применимость MongoDB к части наших задач.
React/Redux -- фронтальная часть.
UI стили и компоненты -- выберем позже.

Начальные проекты

ng-back -- создан на основе шаблона TypeScript Node Starter. Требует запущенного сервера MongoDB (устанавливается отдельно).

ng-front -- создан на основе шаблона Create React App c опцией для TypeSript. Подробности по использованию React/Redux вместе с TypeScript  собраны тут.

ng-lingva -- запросы к данным на естественном языке.

ng-scratch -- визуальное проектирование/кодирование алгоритмов.

PS: ng stands for Next Gedemin or New Gedemin.

16 окт. 2017 г.

7 окт. 2017 г.

Новое в gedemin v. 2.9.4. См. историю изменений.

28 июл. 2017 г.

DATABASE TRIGGERS в Гедымине

Буквально на днях появится возможность создавать в Гедымине DATABASE TRIGGERS. Такие триггеры могут быть назначены на следующие события:
  • CONNECT
  • DISCONNECT 
  • TRANSACTION START
  • TRANSACTION COMMIT 
  • TRANSACTION ROLLBACK
Один из возможных сценариев использования. Представим программу гостиничного бронирования с которой одновременно работают несколько операторов. Бронирование происходит в диалоговом окне, где выбирается номер, период, условия оплаты, дополнительные пожелания. Здесь же вводятся паспортные данные гостей.

Окно работает на своей транзакции, которая по итогу или комитится -- кнопка Ок, или отменяется.

Периоды бронирования заносятся в отдельную таблицу (назовем её BOOKINGS). Здесь хранятся начало, окончание, ссылка на номер и ссылка на клиента. Перед добавлением записи происходит проверка не занят ли уже данный интервал.

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

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

CREATE OR ALTER TRIGGER after_ins_update
  FOR bookings
  ACTIVE
  AFTER INSERT OR UPDATE
  POSITION 32000
AS BEGIN
  RDB$SET_CONTEXT('user_transaction', 'check_tr', 1);
END

Затем, если флаг выставлен, сделаем проверку корректности данных:

CREATE OR ALTER TRIGGER transaction_check
  ACTIVE
  ON TRANSACTION COMMIT
  POSITION 32000
AS BEGIN
  IF (RDB$GET_CONTEXT('user_transaction', 'check_tr') = 1) THEN
  BEGIN
     --   проверяем не пересекаются ли интервалы
     --   если пересекаются, то вызываем EXCEPTION
  END
END

В тексте исключения можно подробно указать пользователю с каким именно бронированием возник конфликт.

8 июл. 2017 г.

Традиционный байдарочный поход

В этом году по живописнейшим местам Налибокской пущи, р. Ислочь.