Форум » LetoDB, HbNetio. » Leto DB Server » Ответить

Leto DB Server

Pasha: Добавил функцию: LETO_GROUPBY(cGroup, cFields, [cFilter], [xScopeTop], [xScopeBottom]) cGroup - имя поля, по которому группируются данные; cFields - список числовых полей через запятую, которые суммируются. Символ # обозначает к-во записей в группе Функция возвращает двумерный массив строк. 1-й элемент каждой строки - значение поля cGroup, следующие элементы суммы полей, заданных в cFields, или к-во записей в группе

Ответов - 232, стр: 1 2 3 4 5 6 7 8 9 10 11 12 All

AlexMyr: alx_on пишет: AlexMyr Странно... Перевыложил Теперь нормально собралось.

AlexMyr: Останавливаю сервер, в логе запись 05/31/11 13:57:23: Send STOP to server... может быть так 05/31/11 13:57:23: Send STOP to server... Done. а то послали stop а результата в логе нет.

AlexMyr: О, увидел, оказывается задержка есть 3 сек. 05/31/11 14:08:22: Send STOP to server... 05/31/11 14:08:25: Server has been closed.


alx_on: Pasha пишет: Версия с потоками куда перспективнее, и она просто должна стать основной Я считаю, что текущая реализация хороша, но до определенных пределов Какие есть недостатки: 1. Запуск отдельного потока для каждого подключения накладно. Как по времени запуска, так и по ресурсам. На машинке (win) с 2Гб у меня запустилось около 800 потоков (вроде много, но если клиентов около 100 и каждый создал по 10 соединений, например, это многопоточное клиентское приложение, то к пределу мы уже подошли) 2. Неэффективное использование ядер (больше времени уходит на переключение между ядрами, чем на работу Хочется переделать на использование ограниченного кол-ва рабочих потоков (в идеале, примерно, равное кол-ву ядер) Плюс оптимизация сетевой подсистемы (с учетом реализации стека TCP/IP конкретной ОС) Но! То что поточная реализация точно лучше старой - это факт.

alx_on: Pasha пишет: надо добавить функцию leto_areaid() Я (пока?) не использую UDF Поэтому добавить то могу, но тестировать не на чем. Да и времени сейчас на это нет, честно говоря. Исправляю и добавляю только по мере необходимости, как это не грустно PS leto_ClearAreaEnv() вызывается каждый раз перед завершением операции Так ли это необходимо? Вызова leto_SetAreaEnv() перед операцией вполне достаточно Какие есть мнения?

Pasha: Да функцию я и сам добавлю А leto_ClearAreaEnv() для версии с двумя потоками был просто необходим, так как рабочая область таблицы для всех пользователей была общая, и ее обязательно надо было сбрасывать Для mt-версии наверное это и не надо.

Pasha: Странно, функция leto_areaid() и не понадобилась Сделал udf-функцию для каскадного обновления (вечером выложу): [pre]/* * UDF_UpdCascade - cascade update key fields in main and relation table * Parameters: * nRecNo - record number in the main table * cKeyField - field name in the main table (primary key) * xKeyNew - new value of key field * cClientAlias - client alias of the relation table * cKeyField2 - field name in the relation table (foreign key) * xOrder - order name or order number in the relation table * * This function return array of record buffer in two tables * Call from client: * * aRecBuf := Leto_Udf("UDF_UpdCascade", ... ) * (table1)->( leto_ParseRec( aRecBuf[1] ) ) * (table2)->( leto_ParseRec( aRecBuf[2] ) ) */ FUNCTION UDF_UpdCascade( nUserStru, nRecNo, cKeyField, xKeyNew, cClientAlias, cKeyField2, xOrder ) LOCAL xKeyOld, cLetoAlias, cArea := Alias() LOCAL nPos := FieldPos( cKeyField ), nPos2 dbGoto( nRecNo ) xKeyOld := FieldGet( nPos ) IF xKeyOld != xKeyNew .and. leto_RecLock( nUserStru, nRecNo ) FieldPut( nPos, xKeyNew ) leto_RecUnlock( nUserStru, nRecNo ) cLetoAlias := leto_Alias( nUserStru, cClientAlias ) dbSelectArea( cLetoAlias ) IF Empty( cKeyField2 ) cKeyField2 := cKeyField ENDIF nPos2 := FieldPos( cKeyField2 ) IF ! Empty( xOrder ) ordSetFocus( xOrder ) ENDIF WHILE dbSeek( xKeyOld ) IF leto_RecLock( nUserStru, RecNo() ) FieldPut( nPos2, xKeyNew ) leto_RecUnlock( nUserStru, RecNo() ) ELSE EXIT ENDIF ENDDO dbSeek( xKeyNew ) dbSelectArea( cArea ) ENDIF RETURN { leto_rec( nUserStru ), (cLetoAlias)->(leto_rec( nUserStru)) }[/pre] И оказалось, что при выборе текущей раб.области pUStru->pCurAStru сам присваивается, только мне непонятно как

alx_on: Pasha пишет: pUStru->pCurAStru сам присваивается, только мне непонятно как leto_Alias() - вызывает leto_SelectArea(), там все делается IF ! Empty( xOrder ) ordSetFocus( xOrder ) ENDIF WHILE dbSeek( xKeyOld ) Вот здесь я был бы осторожнее, т.к. не факт, что от другого UDF не остались установленные фильтры и скопы Лучше все сбрасывать перед операциями поиска и прохода по записям Кстати, недостатки текущей реализации UDF: 1 неудобно каждый раз вручную обновлять файл UDF на сервере 2 требуется рестарт сервера 3 непонятно какая именно версия UDF на сервере Как такая идея работы с UDF: 1. Написать функцию отдачи версии UDF-файла (или каждая UDF-функция должна уметь вернуть свою версию) 2. Написать функцию обновления версии UDF-файла (соответственно сервер должен уметь перечитать его) 3. удаление UDF-файла

Pasha: По поводу areaid - не все так просто. leto_rec использует текущий алиас - pArea, и текущую pUStru->pCurAStru. А они в UDF_UpdCascade оказываются разные: AREASTRU для обоих вызовов одинаковая. Так что запрос areaid и ее установка через leto_selectarea() все-таки нужна. Может лучше передавать ее параметром (необязательным) в leto_rec, leto_reclock, leto_recunlock ? Насчет фильтров - ведь каждая udf-функция вызывается по отдельному запросу с клиента, и после ее работы отрабатывается FreeArea, которая должна все сбросить. Это получается как для leto_ClearAreaEnv(). Установленный фильтр надо проверять разве что если одна UDF-функция вызывается из другой. Reload/Unload для UDF сделаю, по аналогии с отработки команды stop - LETO_STOPSERVER. Там надо вызывать hb_hrbunload/hb_hrbload По поводу версии UDF. Может в hrb-модуль добавить функцию UDF_Version, которая возвращала бы строку версии, и с клиента ее можно опросить обычным вызовом leto_Udf('UDF_Version') ? Кстати, можно еще определить функию UDF_INIT, которую сервер вызывал бы при загрузке UDF (если она есть в модуле, конечно)

Pasha: alx_on пишет: 2. Написать функцию обновления версии UDF-файла (соответственно сервер должен уметь перечитать его) А, это запросом с клиента обновлять UDF ? Можно и так, добавить команду, и с ней передавать hrb-файл, который сервер бы загружал

alx_on: Pasha пишет: leto_rec использует текущий алиас - pArea, и текущую pUStru->pCurAStru. Вот именно. Т.е. использование UDF требует аккуратности (мало ли где еще неявно что то используется) Например можно вместо: dbSelectArea( cArea ) ENDIF RETURN { leto_rec( nUserStru ), (cLetoAlias)->(leto_rec( nUserStru)) } написать так: LOCAL aRet := {,} ....... ....... aRet[2] := leto_rec( nUserStru ) dbSelectArea( cArea ) aRet[1] := leto_rec( nUserStru ) ENDIF RETURN aRet Плюс правильно отработать условие "IF xKeyOld != xKeyNew .and. leto_RecLock( nUserStru, nRecNo )" - (пустой массив) Сейчас просто будет падать (cLetoAlias == NIL) после ее работы отрабатывается FreeArea, которая должна все сбросить. Это получается как для leto_ClearAreaEnv() leto_FreeArea() не сбрасывает ничего (явно, по крайней мере), она только передает таблицу в общее пользование потокам Может в hrb-модуль добавить функцию UDF_Version Как вариант, да согласен

Pasha: Обновление обьекта tbrowse происходит так: go top перемещение в начало, вывод строки skip 1 вывод строки skip 1 ... skip -n возврат на первую строку Применительно к letodb это будет выглядеть так: go top выборка записи с сервера skip 1 n записей со 2-й выбираются с сеовера skip 1 запись берется из буфера без обращения к серверу ... skip -n записи с n до 2 беоутся из буфера, а 1-я вновь запрашивается с сервера Чтобы избежать лишнего запроса, я сделал буферизацию записи, которая получена при gotop/gobottom/goto/seek Таким образом, теперь при стабилизации tbrowse к letodb клиент посылает всего 2 запроса: gotop skip Конечно, при этом буфер для skip должен быть не меньше, чем количество строк в tbrowse Для этого можно вызвать: leto_SetSkipBuffer( oB:rowCount() ) Заодно добавил оптимизацию dbGoto: если запрашиваемая запись есть в буфере, она не считывается с сервера.

Pasha: Помимо буферизации skip хочется добавить в letodb буферизацию seek. Предполагается делать буферизацию исключительно на клиенте, сервер при этом задействован не будет. После выполнения seek, если это не поиск по частичному ключу и не поиск последнего ключа, результат поиска добавляется в буфер. Время актуальности буфера - BUFF_REFRESH_TIME (1 сек) Если ключ для seek найден в буфере, то запрос к серверу не выполняется, а запись берется из буфера. Буфер хранится в LETOTAGINFO. При записи в таблице буфер очищается. Использование seek buffer будет целесообразно для небольших справочников, для которых постоянно вызывается seek, а также при задании set relation. Для р/о, в которой задано использование seek buffer, количество мелких запросов к серверу (seek) может быть сокращено в десятки раз. В Ads, насколько я знаю, подобного механизма нет. Какие будут мысли, идеи, сомнения ? Стоит ли использовать такой механизм по умолчанию для всех р/о, или только для заданных ? Как ограничивать размер такого буфера ?

Pasha: Добавил поддержку seek-буфера. По умолчанию он выключен. Чтобы его включить, надо вызвать leto_setseekbuffer( nRecs ) для соответствующего индекса. Параметр - максимальное количество записей в буфере. Использование такого буфера для таблиц, в которых используется преимущественно индексный метод доступа, позволяет существенно сократить количество запросов к серверу. В моем тесте для не очень большой выборки число "попаданий" в буфер достигало тысяч.

alx_on: Pasha пишет: Добавил поддержку seek-буфера 1. поменялся фильтр (выполняемый на сервере) 2. поменялся scope 3. поменяли видимость удаленных записей в этих случаях буфер поиска необходимо сбрасывать (или не учитывать)

Pasha: alx_on пишет: 1. поменялся фильтр (выполняемый на сервере) 2. поменялся scope 3. поменяли видимость удаленных записей в этих случаях буфер поиска необходимо сбрасывать (или не учитывать) Сделаю. Заодно и для skipbuf, там тоже этого нет. set deleted перехватывать не получится, так что прийдется добавить этот флаг в буфер, и проверять его при использовании буфера. Сделаю отдельную структуру для буфера. Зодно хочется добавить статистику запросов для р.о на клиенте - количество запросов, количество попаданий в буфер

Pasha: Сделал Вызов leto_SetSkipBuffer() и leto_SetSeekBuffer теперь возвращает статистику использования буфера для текущей р/о или индекса: сколько раз записи выбирались из буфера, а не запрашивались с сервера.

Pasha: Собрал letodb под win64 с помощью mingw64. Работает.

alx_on: Pasha пишет: Собрал letodb под win64 с помощью mingw64. Работает у меня и раньше работало (правда MAC OS X 64-бита и gcc) есть какая то разница между mingw64 и gcc?

Sergey Spirin: Pasha пишет: Собрал letodb под win64 с помощью mingw64. Работает. Паш, и чего, ни разу приведения pointer к int по всему коду не было? Абалдеть! P.S. Не советую тешиться



полная версия страницы