Отправлено: 09.02.18 19:01. Заголовок: Dima пишет: А что е..
Dima пишет:
цитата:
А что есть какие то полезные плюшки в форке в отличии от нашей ?
На этот вопрос грамотно ответить не смогу, тк плотному знакомству с оригинальным вариантом что то постоянно мешало. Например:
1) необходимость (?) указания для каждой операции адреса коннекта для каждого файла, например: вместо привычного USE tablename ... писать USE (cConnectPath+"tablename") ... Может это и не так на самом деле, но в каждом примере фигурировал примерно такой синтаксис команд. Слишком много кода перелелывать ради не очень понятной перспективы. В форке делается один раз Leto_Connect("191.168.x.x:2812") и всё. Все остальные USE, INDEX, SELECT и тп работают вообще без каких либо модификаций по сравнению с привычным DBFNTX. Единственное что - пришлось кое-где пошаманить с фильтрами, чтобы они фильтровали на сервере, а не на клиенте. Для этого заменяю потихоньку, одно за другим, выражения вроде:
SET FILTER TO (table->date >= d1) .AND. (table->date <= d2)
на
tmp := "(table->date >= 0d" +DTOS(d1)+ ") .AND. (table->date <= 0d" +DTOS(d2)+ ")" SET FILTER TO &tmp
Процесс творческий и небыстрый, зато результат на удивление приятный в плане скорости работы.
2) Мелочь конечно, но сама идея передавать при каждом вызове Leto_Somefunc(...) неведомую мне UserStru противоречит моим принципам программирования. Если есть некий набор данных, который нужен серверу, а не клиенту - зачем его хранить на клиенте и каждый раз передавать серверу при вызове функции ? Пусть сервер его сам у себя и хранит. Места в памяти и на диске достаточно.
Ну и пара мелочей:
3) Рольф организовал "двухканальный" обмен данными между клиентом и сервером. Т.е. после каждой операции передачи пакета не теряется время на получение сигнала ACK (пакет получен), а сразу передаётся следующий - один за одним. Для обмена сообщениями о некорректном приеме пакетов (NO-ACK) используется второй порт (обычно 2813). И по нему 99,99% времени ничего не передается вообще - тк в большинстве случаев проблем передачи нет как таковых. В случае получения NO-ACK происходит генерация ошибки стандартным способом. Таким образом, вместо миллионов ACK-ACK-ACK..., забивающих канал связи и фрагментирующих пакеты - клиент и сервер изредка (!) кидают NO-ACK по соседнему порту, что должно давать прирост производительности. Я лично не проверял, но почему-то верю ... )))
4) Не знаю, было ли реализовано в оригинале, но Рольф рекомендует после Leto _Connect(...) сразу вызывать Leto_ToggleZip(1) - чтобы включить быстрый алгоритм LZ4 для сжатия пакетов на лету. При копировании обычных dbf дает прирост производительности 20-40%, что само по себе неплохо. Это я проверял лично.
Вот такие мои впечатления за примерно полгода знакомства с "crazy russians db engine, modified with german accuracy..." или что то типа того, как писал Рольф.
Отправлено: 09.02.18 21:07. Заголовок: Sergy пишет Для этог..
Sergy пишет
цитата:
Для этого заменяю потихоньку, одно за другим, выражения вроде:
это, точно, надо делать ? в примере test_filt.prg есть
PRIVATE nNumTop, nNumBot ... nNumTop := 1004 nNumBot := 1010 SET FILTER TO NUM >= nNumTop .AND. NUM <= nNumBot ? "with FORCEOPT = .F." ? DbFilter(), "; optimized:", LETO_ISFLTOPTIM()
? #ifndef __XHARBOUR__ /* -> RTE cause of missing filter sync */ SET( _SET_FORCEOPT, .T. ) #endif SET FILTER TO NUM >= nNumTop .AND. NUM <= nNumBot ? "with FORCEOPT = .T." ? DbFilter() ? "--> optimized:", LETO_ISFLTOPTIM() ...
readme_rus.txt
цитата:
функции dbSetFilter(). Фильтр, который может быть выполнен на сервере, называется оптимизированным. Если фильтр не может быть выполнен на сервере, он является неоптимизированным. Такой фильтр является медленным, поскольку с сервера все равно запрашиваются все записи, которые затем фильтруются на клиенте. Чтобы задать оптимизированный фильтр, необходимо, чтобы в логическом выражении для фильтра отсутствовали переменные или функции, определенные на клиенте. Чтобы проверить, является ли фильтр оптимизированным, надо вызвать функцию LETO_ISFLTOPTIM().
т.е. в "нашей" версии из за переменных фильтр не оптимизирован, а тут показан, что оптимизирован.
Не очень понял про отличия "нашей" и "форк" версии в отношении оптимизированных фильтров, но компиляция и запуск test_filt.prg выдает следующее:
Testing filtering for DBFCDX
ordSetFocus( "NAME" ) seek 1001 Petr 09/02/18 - Ok seek 1010 Andrey 18/02/18 - Ok
NUM >= 1004 .AND. NUM <= 1010 ; optimized: .F. go top 1005 Alexey 13/02/18 - Ok go bottom 1008 Vladimir 16/02/18 - Ok seek 0 / / - Ok seek 1010 Andrey 18/02/18 - Ok
with FORCEOPT = .F. NUM >= nNumTop .AND. NUM <= nNumBot ; optimized: .F.
with FORCEOPT = .T. NUM >= nNumTop .AND. NUM <= nNumBot --> optimized: .F. go top 1005 Alexey 13/02/18 - Ok go bottom 1008 Vladimir 16/02/18 - Ok seek 0 / / - Ok seek 1010 Andrey 18/02/18 - Ok
cName $ NAME ; 'Alex' $ NAME - optimized: .F. go top 1003 Alexander 11/02/18 - Ok go bottom 1005 Alexey 13/02/18 - Ok seek 0 / / - Ok count 42 - Ok
Отправлено: 10.02.18 00:01. Заголовок: До серверных процеду..
До серверных процедур (RPC, UDF) и обмена переменными пока не дошел.
Пока изучаю возможности фонового взаимодействия с LetoDB сервером в отдельных потоках.
В данный момент - не очень понимаю, что принципиально мешает передавать от клиента на сервер массивы и хэши. Пусть даже в текстовом виде. "Наверху" пусть примут строку, преобразуют ее один раз в переменную, а дальше - у меня много таких вот конструкций:
SET FILTER TO table->client $ hListSelectedClients
Работает шустро, удобно и понятно: в хэш можно выбрать хоть одного клиента, хоть сотню - на производительности это почти никак не скажется. ASCAN() по массиву выбранных клиентов тормозит: чем больше клиентов, тем сильнее.
Сам бы подкинул Рольфу идею, но похоже, что я ему уже мозг вынес и он поставил меня в игнор...
Отправлено: 10.02.18 02:40. Заголовок: Sergy пишет что прин..
Sergy пишет
цитата:
что принципиально мешает передавать от клиента на сервер массивы и хэши
массивы передаются, как параметры в UDF функции и возврат обратно. массив в hash в udf получить просто, т.е. исп. вместо set filter ... udf функцию для уст.\снятия фильтра с hash. еще вариант, можно исп. leto_SetEnv( xScope, xScopeBottom, xOrder, cFilter, lDeleted ) aRecNo := leto_dbEval() leto_ClearEnv( xScope, xScopeBottom, xOrder, cFilter ) в "нашей" версии aRecNo положить в SkipBufer и обработать записи DO WHILE ! eof() ... SKIP ENDDO в "форк" не знаю, можно ли так делать ?
LETO_PARSEREC( cRecBuf ) --> nil LETO_PARSERECODRS( cRecBuf ) --> nil не увидел, не есть хорошо режим работы * UDF_dbEval function returns buffer with records by order <xOrder>, and for condition, * defined in <xScope>, <xScopeBottom>, <cFilter>, <lDeleted> parameters * Function call from client:
массивы передаются, как параметры в UDF функции и возврат обратно. массив в hash в udf получить просто, т.е. исп. вместо set filter ... udf функцию для уст.\снятия фильтра с hash. еще вариант, можно исп. leto_SetEnv( xScope, xScopeBottom, xOrder, cFilter, lDeleted ) aRecNo := leto_dbEval() leto_ClearEnv( xScope, xScopeBottom, xOrder, cFilter ) в "нашей" версии aRecNo положить в SkipBufer и обработать записи DO WHILE ! eof() ... SKIP ENDDO в "форк" не знаю, можно ли так делать ?
Больше 20 лет занимаюсь Clipper/Harbour - но честно говоря, почти ничего не понял из вышесказанного, сорри
LETO_PARSEREC( cRecBuf ) --> nil LETO_PARSERECODRS( cRecBuf ) --> nil не увидел, не есть хорошо режим работы
* UDF_dbEval function returns buffer with records by order <xOrder>, and for condition, * defined in <xScope>, <xScopeBottom>, <cFilter>, <lDeleted> parameters * Function call from client:
можно заменить на cKli := ',' while !eof() cKli += alltrim(table->client)+',' skip end потом SET FILTER TO &( ','+table->client+',' $ '"'+cKli+'"' )
В паре мест делаю примерно похожим образом, но это 'костыли': чем длиннее строка, тем медленнее она будет обрабатываться. Хотелось-бы нормальное решение.
Отправлено: 10.02.18 15:41. Заголовок: Dima пишет: В Leto ..
Dima пишет:
цитата:
В Leto Павел делал BM фильтры , не подходит ?
Поддержка rushmore bitmap фильтров в форке никуда не делась и для многократной выборки - вполне: сначала выбрать нужные записи, потом уже по ним работать. Например в DbEdit().
А для единичного отчета, когда Пете нужен отчет по всем его клиентам из Пензы за текущий квартал, а Васе - из Уфы за предыдущий год, то получается у каждого - минимум два прохода по таблице продаж. Причем первый из них - так и не оптимизирован хэшем, а второй - вообще лишний.
Leto сервер может подключать hrb файл с пользовательскими функциями при наличии оного. Пример letoudf.hrb, получаемый из letoudf.prg. Функции вызываются с клиента, исполняются на сервере. Им могут передаваться параметры типов: C,N,L,D,A, которые можно применять на сервере. В "нашей" версии первым в функцию передается nUserStru. Условно можно назвать номером соединения со стороны сервера с клиентом. Кроме получения данных соединения, nUserStru можно исп. как префикс файлов (подкаталог) для получаемых на сервере файлов выборок и возврата на клиента списка имен файлов, для открытия и дальнейшего исп. на клиенте, с удалением после работы. То, что в "форк" убран nUserStru, не уверен что это хорошо, надо посмотреть.
В "форк" LETO_DBEVAL( [ <cBlock> ], [ <cFor> ], [ <cWhile> ], [ nNext ], [ nRecord ], [ lRest ] ) ==> aResults возвращает массив RecNo для исп. в BM фильтрах или собственными dbGoto(...) В "нашей" версии кроме BM фильтров и dbGoto(...) есть механизм на сервере заполнения SkipBuffer (это буфер для nn записей), исп. при работе leto серверов (можно уст. кол-во строк в буфере есть ф-я). По этому буферу идет работа на клиенте dbSkip() и т.д.. Формируем SkipBuffer выполнением ф-ии на сервере leto_ParseRecords( leto_Udf('UDF_dbEval', <xScope>, <xScopeBottom>, <xOrder>, <cFilter>, <lDeleted> ) ) на входе leto_ParseRecords строка. Получается аналог SET FILTER TO ..., т.е. минимум изм. кода старой проги. Далее работа в цикле. В "форк" отсутствие leto_ParseRecords, по мну, БАААЛЬШОЙ минус, не смертельный, но ...
Более-менее понятно, но очень уж зависимо от конкретной реализации. Пытаюсь по максимуму использовать стандартизованный интерфейс харборовских RDD и не использовать без особой необходимости Leto_xxx(). Ведь если через пару-тройку лет появится какой-нибудь WinterDB - опять всё переписывать?
У меня половина юзеров работает с базой удаленно, через RDP. Для них - данные локальны и выборка идет через dbfntx. Другя половина - как обычно, через сеть. Сначала полностью через dbfntx, сейчас - без особой спешки все больше и больше операций переключаю на LetoDBf.
Форк интересен (для меня) тем, что открыв таблицу один раз VIA "dbfntx" (rdp-локально) или VIA "leto" (по сети) я больше ничего не модифицирую в коде. Максимум - оптимизирую фильтр для выполнения на сервере. Но он точно так же будет работать как с leto, так и с dbfntx.
Пока оставил на "сладкое" некоторые моменты в программе, которые нельзя решить "влоб": как например, не могу отказаться от простого фильтра по хэшу или фильтра, реагирующего на нажатия кнопок на клавиатуре (живой поиск), например:
SET FILTER TO MyFilter()
Да, сделав специфические заточки под Leto, я потеряю совместимость с dbfntx. Пока непонятно - будет ли стоить овчинка выделки. Проект большой и раскорячить его мне не хотелось бы. В данный момент у меня даже сетевые юзеры при отсутствии коннекта с LetoDB получат сообщение при входе, но продолжат работать, как ни в чем ни бывало. Чуть медленнее, чем "обычно" (к которому они уже привыкли), но работать.
Понятно, что и локальных rdp-юзеров можно переключить полностью на локальный Leto сервер. И более того - с точки зрения целостности данных так и сделаю. Но процесс перевода небыстр, а для юзеров нужны всё новые и новые фичи, а не только ускорение старых.
Отправлено: 11.02.18 10:01. Заголовок: Sergy пишет опять вс..
Sergy пишет
цитата:
опять всё переписывать?
можно исп. обертки функции\классы\препроцессор(leto_file.ch в "форк") If RddName() == 'LETO' ... Else ... EndIf исп. leto_sum(...), leto_group(...), можно пробнуть адаптировать их под работу без сервера или Пашу попросить это сделать, если очень надо.
цитата:
Но процесс перевода небыстр, а для юзеров нужны всё новые и новые фичи, а не только ускорение старых.
На новые фичи перейти мне удалось только внедрением letodb с новой организацией базы на cdx (старая ntx), т.е. загрузка на сервер старой и организация переноса изменений (записи таблиц) с новой базы в старую. Новые фичи работают на letodb, старые как было, постепенно старые режимы переключаю на letodb
Все даты в формате GMT
3 час. Хитов сегодня: 19
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет