18 октября, четверг 16:32
Bankir.Ru

Объявление

Свернуть
Пока нет объявлений.

longsaver - описание

Свернуть
X
  • Фильтр
  • Время
  • Показать
Очистить всё
новые сообщения

  • longsaver - описание

    День добрый, уважаемые.

    не поделится ли кто своими знаниями по созданию longsaver и как он дружит с "farmsql". Или может доки есть по этому. Буду весьма признателен.

    спасибо.

  • #2
    Дружбу LS и FarmSQL показываю на примере.
    Имеющуюся доку прилагаю.

    Код:
    /* заводим переменные, соответствующие полям будущего SELECT  
      (для примера будем делать выборку из Accounts)
      внимание ! Для строк обязательно указывать конкретную длину */
    Var
      fClientCode : LongInt;
      fNewAccNum  : string[25];
      fAccName    : string[200];
      fOpenDate   : Date;
      
    const
      LSId='Уникальное имя для LS';
      
    formula
    {
      //создаем LS (результат=true - успешно)
      CreateLS(LSId, //первый параметр - идентификатор LS
                     //вторым параметром идет цепочечный (вложенный) вызов LField - задаем поля
                     //связывая их с переменными
               LField(PField(@fClientCode),
               LField(PField(@fNewAccNum),
               LField(PField(@fAccName),
               LField(PField(@fOpenDate), 0)))), //0 - признак конца цепочки полей
                     //третий параметр - цепочка сегментов индекса (поля нумеруются с нуля)
               KeySeg(0, KeySeg(1, 0)), //определили индекс: fClientCode+fNewAccNum (0+1)
               10,10,false);  //магическое завершение CreateLS :)
               
      //выбираем текущий LS (можно проверить результат на true)
      SetTarLS(LSId);
      
      //готовим SELECT
      ClearFarmSQL;
      AddFarmSQL('SELECT ClientCode,NewAccNum,AccName,OpenDate FROM Accounts WHERE ...чего-то там...');
      
      TLSFillBySQL; //выполняем SELECT - результат попадает в LS
      
      if TLSCount=0 Exit; //проверили число записей в LS
      
      if TLSGetFirst //поля первой (по индексу) записи попадают в связанные переменные
      do
      {
        //что-то там делаем с fClientCode, fNewAccNum, fAccName, fOpenDate 
        //очередной записи
        // ....
      } while TLSGetNext;  //пихаем в связанные переменные очередную запись
                           //true=успешно,  false=записи кончились
      DisposeLS(LSId); //не забываем убрать мусор
    }

    Комментарий


    • #3
      Огромное человеческое спасибо. даже не мог подумать, что имеется подобная документация.

      Комментарий


      • #4
        Возник вопрос как раз по связке LongSaver и FarmSQL.
        Не все там гладко почему-то.
        Допустим есть такой кусок программы:
        Код:
        CreateLS(.....); SetTarLS(...);
        while много тысяч раз> do
        {
          TLSDeleteAll;
          ClearFarmSQL;
          AddFarmSQL('select * from ... чего-то там выбираем');
          if TLSFillBySQL
          { ... обрабатываем полученные в LongSaver данные
          };
        }
        Так вот замечен странный глюк. На первых итерациях цикла все идет нормально, данные успешно выбираются в LongSaver. Однако, после какой-то N-ой попытки (причем N достаточно велико, больше нескольких сотен), на очередном N+1 (и всех последующих) прогонах цикла функция TLSFillBySQL вдруг начинает выдавать значение FALSE, LongSaver остается пустым. При этом условие селекта заведомо правильное, записи должны выбираться.
        Такое ощущение, что что-то где-то в какой-то момент переполняется.
        Кто-нибудь знает, в чем именно тут засада ?

        Комментарий


        • #5
          heg
          Вместо
          TLSDeleteAll
          попробуйте использовать
          DisposeLS. М.б. поможет.

          Комментарий


          • #6
            Chikov попробуйте использовать DisposeLS м.б. поможет
            Нет, не помогло.
            Вобщем я не поленился и провел многочасовое тестирование этого вопроса. Мои выводы: где-то в реализации функции TLSFillBySQL имеется утечка памяти. Или, возможно, она есть в сборке мусора LS.

            Теперь, если интересно, подробности.
            Для тестирования мы написали вот такую программу-бенчмарк:
            Код:
            //тестовая программа, демонстрирующая ошибку при многочиленных TLSFillBySQL
            const
              LSId='ikjdhhgsdsn4834';
            
            var
              i,c:longint;
              fAccNum:string[10];
              fCurrCode:string[3];
              fNewAccNum:string[20];
              fClientCode:longint;
              fClientName:string[80];
              fAccName:string[200];
              fOpenDate:Date;
            
            formula
            {
              DisposeLS(LSId);
              CreateLS(LSId,
               LFIeld(PField(fAccNum),
               LField(PField(fCurrCode),
               LField(PField(fNewAccNum),
               LField(PField(fClientCode),
               LField(PField(fClientName),
               LField(PField(fAccName),
               LField(PField(fOpenDate), 0··········)),
               KeySeg(6, KeySeg(2,0)), 10, 10, false);
              SetTarLS(LSId);
              i:=0;
              AtStartVisual(vfRotate,vfTimer,'',1);
              while i=1000000 do //цикл для массовости
              {
                i:=i+1;
                c:=40100;
                while c=40999 do //цикл по счетам 2-порядка
                {
                  TLSDeleteAll;
                  ClearFarmSQL;
                  //делаем некую тестовую выборку из Accounts и Clients
                  AddFarmSQL('select a.AccNum,a.CurrCode,a.NewAccNum,a.ClientCode,c.ShortName,');
                  AddFarmSQL(' a.AccName,a.OpenDate from Accounts a, Clients c where');
                  AddFarmSQL('  SubStr(a.NewAccNum,1,5)='''+string(c,'77777')+''' and a.ClientCode=c.clientcode');
                  if not TLSFillBySQL
                  {
                    //вот здесь-то и вылезла ошибка
                    AtStopVisual('',0);
                    StrMessage('Застряли на i='+string(i)+', c='+string(c),mfIOk);
                    DisposeLS(LSId);
                    Halt;
                  };
                  if (TLSCount>0) and TLSGetFirst
                  do
                  {
                    AtINextVisual(string(i)+' '+fNewAccNum);
                    //здесь должна быть имитация бурной полезной деятельности
                  } while TLSGetNext;
                  c:=c+1;
                };
              };
              AtStopVisual('',0);
              DisposeLS(LSId);
            }
            Практического смысла в ней никакого, кроме того, что она много-многократно делает некие выборки из таблицы Accounts и сохраняет их в LS.
            Тестирование велось на версии 8.800\40 под Oracle 9.2. Общее количество записей в таблице Accounts составляло примерно 10000. В выборки на каждой итерации внешнего цикла реально попадало около 4000 записей (суммарно за один прогон внешнего цикла). Рабочая станция - двухядерный P4 c 2 Гб ОЗУ.

            Для начала мы запустили программу в вышеприведенном виде (с TLSDeleteAll) на ночь. Программа отработала более 4 часов и остановилась (т.е. очередной вызов TLSFillBySQL вернул FALSE и заполнять LS отказался) на 1007-й итерации внешнего цикла. При этом утром некоторые другие программы на рабочей станции выдавали сообщения о нехватке ОЗУ, и компьютер пришлось перезагрузить.
            На следующий день мы провели второй эксперимент.
            Вызов TLSDeleteAll заменили на связку DisposeLS+CreateLS+SetTarLS и запустили программу на этот раз днем, наблюдая за расходом памяти посредством стандартного диспетчер задач Винды. Тут стала заметна утечка памяти. По данным диспетчера задач память, отведенная для работающего paydoc.exe, увеличивалась примерно на 0.5 Мб на каждую итерацию внешнего цикла, и не освобождалась. Программа остановилась (TLSFillBySQL=FALSE) после 8 с лишним часов работы, на 718-й итерации внешнего цикла, при этом pyadoc.exe успел сожрать более 528 Мб памяти.

            Выводы:
            1. TLSDeleteALL работает почти в 2 раза быстрее, чем процедура уничтожения LS и его последующего создания.
            2. Где-то в связке между FarmSQL и LS имеется утечка памяти. Или не отрабатывает сборка мусора.

            Комментарий


            • #7
              heg
              А собрать все за один раз не судьба? Что за задачу Вы решаете. Зачем вообще циклы нужны?!

              Комментарий


              • #8
                Конечно судьба. Лично у нас уже все нормально.
                Задача сейчас уже не важна - просто нужно было активно работать из Кворума со своими собственными таблицами, которых нет в словаре. И я, как привычный к Бтриву человек, сначала попытался сделать все максимально из-под Атлантиса, реализуя обращения к своим таблицам через FarmSQL. Ну и натолкнулся на такой интересный факт, что многочисленные вызовы selectов из-под Атлантиса с какого-то момента перестают работать.

                А решение конечно есть - оптимизировать схему запросов и вынести большую часть работы с данными из Атлантиса на сервер, в хранимые процедуры. Что мы собственно потом и сделали, и все заработало нормально.

                Я поднял вопрос в этой теме не чтобы лично мне помогли написать правильно отчет, а чтобы люди были в курсе - есть такая проблема.
                Чтобы банковские автоматизаторы помнили - выборка в LS из FarmSQL не на 100% надежная операция, ее опасно использовать в чрезмерно больших количествах.
                Ну и чтобы разработчики были в курсе - есть еще простор для поиска и устранения ошибок в Кворуме.

                Комментарий


                • #9
                  heg
                  Нужно использовать вот это или все делать на сервере:

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

                  Сервисные функции:

                  function CreateQuery(Id: string; Cache: integer): boolean;
                  Создать неявный курсор (Id - идентификатор курсора, Cache - количество записей, одновременно загружаемых с сервера на клиент)

                  Function SetTarQuery(Id:string):boolean;
                  Сделать текущим курсор с идентификатором Id

                  Procedure DisposeQuery(Id:string);
                  Убить курсор с идентификатором Id

                  Function QueryExist(Id:string):boolean;
                  Проверить существует ли курсор с идентификатором Id


                  function SQL_ClearSQLText(Id: string): boolean;
                  Очистить текст запроса для курсора с идентификатором Id

                  function SQL_AddSQLText(Id: string; sql:String): boolean;
                  Добавить в текст запроса для курсора с идентификатором Id строку sql

                  procedure SQL_ShowSQLText(Id: string);
                  Показать текст запроса для курсора с идентификатором Id

                  function SQL_ExecuteQuery(Id: string): boolean;
                  Выполнить запрос

                  function SQL_EOFQuery(Id: string): boolean;
                  Функция конца выборки

                  function SQL_NextQuery(Id: string): boolean;
                  Сделать Fetch

                  То же самое, только для текущего курсора
                  function ClearSQLText: boolean;
                  function AddSQLText(sql:String): boolean;
                  procedure ShowSQLText;
                  function ExecuteQuery: boolean;
                  function EOFQuery: boolean;
                  function NextQuery: boolean;


                  Для текущего курсора:

                  function FieldAsString(FieldId: string): string;
                  Возвращает значение поля FieldId в виде строки

                  function FieldAsFloat(FieldId: string): Double;
                  Возвращает значение поля FieldId в виде Double

                  function FieldAsInteger(FieldId: string): Integer;
                  Возвращает значение поля FieldId в виде Integer

                  function FieldAsDate(FieldId: string): Date;
                  Возвращает значение поля FieldId в виде даты

                  Комментарий


                  • #10
                    Chikov
                    Вы будете смеяться, но приведенная выше многоцикловая программа-бенчмарк, специально переписанная под Query, падает еще быстрее, чем при FarmSQL+LS. Причем там даже не срабатывает контроль на ExecuteQuery=FALSE - просто падает с ошибкой EOutOfMemory.
                    Может я что-то не так делаю, но DisposeQuery+CreateQuery на каждом шаге цикла абсолютно не помогают.

                    Короче, не хочу мучить эту тему с утечкой памяти дальше, на мой взгляд рекомендации банковским автоматизаторам очевидны:
                    1. При использовании SELECTов из-под Атлантиса (хоть через FarmSQL, хоть через Query) нужно строить запросы грамотно с точки зрения использования возможностей SQL - делать меньше запросов по количеству, но зато "более качественные"
                    2. При необходимости использовать сложную логику с кучей SELECTов, желательно большей частью это реализовывать не в Атлантисе, а на серверной логике - там и надежнее, и работать быстрее будет.

                    Комментарий


                    • #11
                      А вот, кстати, хотите еще мааааленькую засадку при вызове хранимых процедур из-под Атлантиса через InitSP...ExecSP ?
                      Правда в ней вины разработчиков ИМХО нет.
                      Из под Атлантиса через InitSP+SetXXXParamSP+ExecSP нельзя вызывать перегруженные функции, т.е. те процедуры или функции, для которых на сервере есть несколько разных определений с разными наборами параметров, но с одним и тем же именем.
                      Сам Оракл вполне корректно работает с перегруженными функциями, а вот механизм вызова их из-под Атлантиса может сбоить.

                      Комментарий


                      • #12
                        А чего при выполнении LS_FillBySQL('DOCS') может такого происходить, что "схлопывается" приложение paydoc и приходится перезаходить?

                        Возвращается всего-то 5 строк.

                        Может есть какие-то ограничения на запрос (длина и т.п.)?

                        Спасибо.

                        Комментарий


                        • #13
                          Сообщение от RoverHP Посмотреть сообщение
                          А чего при выполнении LS_FillBySQL('DOCS') может такого происходить, что "схлопывается" приложение paydoc и приходится перезаходить?
                          Возвращается всего-то 5 строк.
                          Может есть какие-то ограничения на запрос (длина и т.п.)?
                          Спасибо.
                          Ошибка может вызываться двумя причинами:

                          1. Несоответствие типов между переменными LongSaver и возвращаемыми запросом данными. Ну, например, запрос возвращает данные типа Date, а соответствующая по порядку переменная LongSaver имеет тип Word. Или, например, запрос возвращает 5 столбцов данных, а в LongSaver описано только четыре.

                          2. Возможны аналогичные проблемы, если в LongSaver включена переменная строкового типа, которая в разделе var описана вот так:
                          var aField : string;
                          Это неправильно. Все строковые переменные, предполагаемые к участию в LongSaver, должны быть описаны с конкретным ограничением длины, вот так правильно:
                          var aField : string[254];

                          Уточнить причину "схлопывания" paydoc можно в логе StackErr.log, который автоматически формируется в текущем каталоге.

                          Комментарий


                          • #14
                            Спасибо большое, heg.

                            Сам бы я долго догадывался.

                            Комментарий


                            • #15
                              Так получилось исправить то ?

                              Комментарий


                              • #16
                                Ага.

                                Поле, которое в запросе вычислялось как:
                                AddFarmSQL(' cur.currsname ||'' ''|| trim(to_char(decode (h.on_dt, 1, h.paysumdb, h.paysumkr), ''99999999D99'')) as amount,');
                                определил изначально как double. Сбило имя поля - amount.

                                Переопределил на string[254] и "зашуршало"

                                Комментарий

                                Пользователи, просматривающие эту тему

                                Свернуть

                                Присутствует 1. Участников: 0, гостей: 1.

                                Обработка...
                                X