24 июл. 2010 г.

Pascal forever

Apple has opened sources of their MacPaint and QuickDraw applications. Naturally, it had been done in Pascal with some assembler insertions. The sources are available from this Computer History Museum page.

14 июл. 2010 г.

Посадили дерево

В наших интервальных деревьях всю жизнь была ошибка. Интервалы перекрывались в следующем случае: есть два элемента A и B, имеющих одного родителя. A расположен левее B. Пусть B имеет два дочерних элемента C и D, примыкающих друг к другу, причем C расположен левее D и интервал C уже D. Тогда, при перемещении D в A, в процессе расширения интервалов вправо, интервал C будет перекрываться с A и B.

KISS

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

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

Новый механизм был создан за два дня, буквально следуя принципу KISS — Keep It Simple Stupid. В его основе объект истории SQL запросов (TgdcSQLHistory) и копия интерфейса борландовского SQL монитора. Везде, где надо, через условную компиляцию мы подменили обращение к модулю IBSQLMonitor.pas на IBSQLMonitor_Gedemin.pas.

Лог трассировки находится в SQL редакторе на соответствующей вкладке. Там же — чек-бокс для включения/выключения и кнопка очистки. Трассировку можно также активировать параметром командной строки /trace.

7 июл. 2010 г.

Жорсткая праўда беларускага інтэрнэту

Адзін з найбуйнейшых айчынных правайдэраў, Деловая Сеть, мае канал у 750 Мбіт/с на... 6 000 кліентаў. Які тут анлім. Калі ўсе разам пойдуць у сеціва атрымаецца па 125 Кбіт/с на брата. У іншых "незалежных" правайдэраў каналы яшчэ вузейшыя. І ўсе разам яны ўпіраюцца ў ліміт знешнега каналу нашага горача любімага манапаліста Белтэлекома, які ў заходнім накірунку складае менш за 16 Гбіт/c. Калі падзяліць на колькасць карыстальнікаў сеціва (4 млн), дык кожнаму дастанецца ажно па 4 (Чатыры!) Кбіт/с.

1 июл. 2010 г.

Дело было не в бобине

Десять лет мы наблюдали редкие-редкие нарушения вложенности по LB, RB на базах наших клиентов. До сих пор считалось, что проблема в незащищенности полей со значениями границ от случайной записи. Однако, тесты выявляют нарушение вложенности и в стерильных лабораторных условиях, когда поля LB, RB уж точно никем сторонним не заполняются.


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

Будем искать.

Первый самостоятельно написанный EXECUTE BLOCK

EXECUTE BLOCK (id INTEGER = :id) 
  RETURNS (companykey INTEGER) 
AS 
  DECLARE VARIABLE ct INTEGER;
BEGIN 
  ct = 4; 
  companykey = 0; 

  WHILE (:ct = 4 AND NOT :companykey IS NULL) DO 
  BEGIN 
    companykey = NULL; 

    SELECT id, parent, contacttype 
    FROM gd_contact 
    WHERE id = :id 
    INTO :companykey, :id, :ct; 
  END 

  SUSPEND; 
END 
Возвращает организацию для заданного подразделения. Хотя, конечно, все можно было сделать простым запросом:
SELECT c.id 
FROM gd_contact c JOIN gd_contact d
  ON c.lb < d.lb AND c.rb >= d.rb
WHERE d.id = :ID AND c.contacttype = 3

16 июн. 2010 г.

Неявные JOIN-ы

Такие вот запросы практиковались в раннем Гедымине:
SELECT
   isg2.RDB$FIELD_NAME       FirstField
   , rc2.RDB$RELATION_NAME   NetTable
   , isg4.RDB$FIELD_NAME     SecondField
   , rc5.RDB$RELATION_NAME   TargetTable
   , isg6.RDB$FIELD_NAME     TargetField 
   , (SELECT 1
     FROM RDB$DATABASE
     WHERE
       EXISTS(SELECT *
              FROM RDB$RELATION_FIELDS rf
              WHERE rc5.RDB$RELATION_NAME = rf.RDB$RELATION_NAME
               AND rf.RDB$FIELD_NAME = 'LB')
       AND EXISTS(SELECT *
              FROM RDB$RELATION_FIELDS rf
              WHERE rc5.RDB$RELATION_NAME = rf.RDB$RELATION_NAME
               AND rf.RDB$FIELD_NAME = 'RB')) IsTree
 FROM
   RDB$INDEX_SEGMENTS isg1
   , RDB$RELATION_CONSTRAINTS rc1
   , RDB$REF_CONSTRAINTS rfc1
   , RDB$RELATION_CONSTRAINTS rc2
   , RDB$INDEX_SEGMENTS isg2
   , RDB$INDEX_SEGMENTS isg3
   , RDB$RELATION_CONSTRAINTS rc3
   , RDB$INDEX_SEGMENTS isg4
   , RDB$INDEX_SEGMENTS isg5
   , RDB$RELATION_CONSTRAINTS rc4
   , RDB$REF_CONSTRAINTS rfc2
   , RDB$RELATION_CONSTRAINTS rc5
   , RDB$INDEX_SEGMENTS isg6 
 WHERE
   rc1.RDB$RELATION_NAME = :tablename
   AND rc1.RDB$INDEX_NAME = isg1.RDB$INDEX_NAME
   AND rc1.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'
   AND rfc1.RDB$CONSTRAINT_NAME = rc2.RDB$CONSTRAINT_NAME
   AND rfc1.RDB$CONST_NAME_UQ = rc1.RDB$CONSTRAINT_NAME
   AND rc2.RDB$INDEX_NAME = isg2.RDB$INDEX_NAME
   AND rc1.RDB$RELATION_NAME <> rc2.RDB$RELATION_NAME
   AND isg3.RDB$FIELD_NAME = isg2.RDB$FIELD_NAME
   AND rc3.RDB$RELATION_NAME = rc2.RDB$RELATION_NAME
   AND rc3.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'
   AND rc3.RDB$INDEX_NAME = isg3.RDB$INDEX_NAME
   AND isg4.RDB$INDEX_NAME = isg3.RDB$INDEX_NAME
   AND isg2.RDB$FIELD_NAME <> isg4.RDB$FIELD_NAME
   AND isg5.RDB$FIELD_NAME = isg4.RDB$FIELD_NAME
   AND rc4.RDB$INDEX_NAME = isg5.RDB$INDEX_NAME
   AND rc4.RDB$CONSTRAINT_TYPE = 'FOREIGN KEY'
   AND rc4.RDB$RELATION_NAME = rc2.RDB$RELATION_NAME
   AND rfc2.RDB$CONSTRAINT_NAME = rc4.RDB$CONSTRAINT_NAME
   AND rfc2.RDB$CONST_NAME_UQ = rc5.RDB$CONSTRAINT_NAME
   AND rc5.RDB$INDEX_NAME = isg6.RDB$INDEX_NAME 
Возвращает список атрибутов типа множество для заданной таблицы.

8 июн. 2010 г.

Предварительные результаты по внешним ключам

Для тестирования была использована действующая база ВМК размером 29,912,520 Кб. Были конвертированы все внешние целочисленные ключи, удовлетворяющие следующим условиям:
  • В таблице более 10 000 записей.
  • Уникальных значений в ключе не более 200.
  • Ключ не ссылается на эту же таблицу.
Всего ключей, удовлетворяющих условиям, набралось 266. После их удаления и процедуры бэкапа-разбэкапа базы ее размер уменьшился до 23,752,136 Кб или на 21%.

После этого мы проверили скорость выполнения запроса на изменение данных на таблице AC_ENTRY, которая содержит более 9 млн. записей. Текст команды приводится ниже:
update ac_entry set
  companykey = 147048034,
  usr$gs_department = 147048036,
  currkey = 147021255,
  usr$gs_service = 147089059
Четыре изменяемых поля — это сконвертированные ссылки. Проверка целостности осуществляется с помощью триггера следующего вида:
CREATE TRIGGER xxx ON AC_ENTRY
  AFTER UPDATE
  POSITION 32000
BEGIN 
  IF (NEW.USR$GS_SERVICE IS NOT DISTINCT FROM 
    OLD.USR$GS_SERVICE) THEN EXIT;

  IF (NEW.USR$GS_SERVICE IS NOT NULL) THEN 
  BEGIN 
    IF (NOT EXISTS (SELECT id FROM gd_ref_constraint_data 
      WHERE value_data = NEW.USR$GS_SERVICE 
        AND constraintkey = 388626603)) THEN 
    BEGIN
      IF (NOT EXISTS (SELECT ID FROM GD_GOOD 
          WHERE ID = NEW.USR$GS_SERVICE)) THEN
        EXCEPTION gd_e_fkmanager 'message';

      RDB$SET_CONTEXT('USER_TRANSACTION', 
        'REF_CONSTRAINT_UNLOCK', '1');
      INSERT INTO gd_ref_constraint_data 
        (constraintkey, value_data, value_count)
        VALUES 
        (388626603, NEW.USR$GS_SERVICE, 1);
      RDB$SET_CONTEXT('USER_TRANSACTION', 
        'REF_CONSTRAINT_UNLOCK', '0');
    END
  END
END
Время выполнения запроса:
  • На новой базе — 1945 сек.
  • На старой базе — 2642 сек.
Получаем ускорение на 26%. Окончательные выводы можно будет сделать после тестирования на 2-3-х других базах.

7 июн. 2010 г.

Тестирование подсистемы отчетов

При тестировании изменений в подсистеме отчетов мы постарались исключить фактор пропускной способности сети и производительности дисковой подсистемы. Использовался встроенный сервер Firebird 2.5. Данные генерировались с помощью хранимых процедур. Пример такой процедуры можно видеть ниже:
CREATE PROCEDURE tst_p_gen_str_data (i INTEGER)
  RETURNS (id INTEGER,
    s1 VARCHAR(60),
    s2 VARCHAR(60),
    s3 VARCHAR(60),
    s4 VARCHAR(60))
AS
BEGIN
  s1 = 'ABC dfg';
  s2 = '123 567 9874 10';
  s3 = 'qwertyuiop asdfghjl; xzcvnbvmn,m.';
  s4 = '';
  WHILE (i > 0) DO
  BEGIN
    id = :i;
    i = :i - 1;
  END
END
Тестировались следующие отчеты:
  1. Отчет по одному набору данных со строковыми полями.
  2. Отчет по одному набору данных с целочисленными полями.
  3. Отчет по двум наборам данных, связанных связью мастер-дитэйл. Наборы данных со строковыми полями.
Замеры выполнялись на втором построении отчета. Учитывалось время построения отчета и информация об использовании оперативной памяти, полученная с помощью программы Process Explorer.

Таблица с результатами находится здесь.

Вывод:
  • для отчетов мастер-дитэйл на двух датасетах разница незначительна — увеличение потребления памяти на 2% и ускорение на 6%.
  • для отчетов, основанных на одном наборе данных, достигается существенное ускорение в 35% при умеренном росте потребления памяти — от 9 до 16%.
Стоит заметить, что Fast Report 4 позволяет строить отчеты с группировкой (фактически, мастер-дитэйл)  с использованием одного набора данных.

1 июн. 2010 г.

Проблема 1 июня 2010 года

В нашей истории случались забавные и не очень случаи, когда программа начинала работать неверно с определенной даты. Как выяснилось, мы не одиноки и проколы случаются с куда более крупными компаниями. Так с 1 июня 2010 г. перестал запускаться Corel X4. На официальном форуме техподдержки дается решение проблемы: "set back the clock 1-2 months".

Отстоим свой интернет!

Создан сайт ukaz60.net для сбора подписей за отмену Указа 60, фактически уничтожающего свободный интернет в Беларуси. Просьба всем небезразличным поддержать!