Форум » LetoDB, HbNetio. » LetoDb fork » Ответить

LetoDb fork

PSP: https://github.com/elchs/LetoDBf https://github.com/elchs/LetoDBf/blob/master/README.md Кто-нибудь пробовал или использует в продакшене?

Ответов - 104, стр: 1 2 3 4 5 6 All

PSP: Попробовал. Наткнулся на вылет OrdKeyNo(), которого не было с "оригинальным" LetoDB. На этом эксперименты пока закончены)

Sergy: Использую, доволен. Оригинальную ("нашу") версию не юзал, начал сразу с форка. Был момент, когда Rolf (elch) пропал куда-то из эфира и стало немного ссыкотно. Сейчас вроде активничает. Если есть что обсудить - welcome.

Dima: Sergy пишет: Оригинальную ("нашу") версию не юзал, начал сразу с форка. А что есть какие то полезные плюшки в форке в отличии от нашей ?


Sergy: 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..." или что то типа того, как писал Рольф.

SergKis: Sergy пишет Для этого заменяю потихоньку, одно за другим, выражения вроде: это, точно, надо делать ? в примере test_filt.prg есть[pre2] 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() ... [/pre2] readme_rus.txt функции dbSetFilter(). Фильтр, который может быть выполнен на сервере, называется оптимизированным. Если фильтр не может быть выполнен на сервере, он является неоптимизированным. Такой фильтр является медленным, поскольку с сервера все равно запрашиваются все записи, которые затем фильтруются на клиенте. Чтобы задать оптимизированный фильтр, необходимо, чтобы в логическом выражении для фильтра отсутствовали переменные или функции, определенные на клиенте. Чтобы проверить, является ли фильтр оптимизированным, надо вызвать функцию LETO_ISFLTOPTIM().

SergKis: PS. Сори, не туда нажал. т.е. в "нашей" версии из за переменных фильтр не оптимизирован, а тут показан, что оптимизирован.

Sergy: SergKis пишет: т.е. в "нашей" версии из за переменных фильтр не оптимизирован, а тут показан, что оптимизирован. Не очень понял про отличия "нашей" и "форк" версии в отношении оптимизированных фильтров, но компиляция и запуск test_filt.prg выдает следующее: [pre2]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 [/pre2]

SergKis: Sergy пишет выдает следующее Ясненько. Я см. только по тексту примера, нет установленного форк, из него понял, как понял Увидел, прицепленный rdd SIXCDX, что не плохо.[pre2] leto_DbDriver( [ <cNewDriver> ], [ <cMemoType> ], [ <nBlocksize> ] ) ==> aInfo убрааны LETO_SETSEEKBUFFER( nRecsInBuf ) ==> 0 ! DEPRECATED ! LETO_SETFASTAPPEND( lFastAppend ) ==> .F. ! DEPRECATED ! не использовал. Leto_FCopyToSrv( cLocalFileName, sServerFileName[, nStepSize ] ) ==> lSuccess Leto_FCopyFromSrv( cLocalFileName, sServerFileName[, nStepSize ] ) ==> lSuccess Copy a file from/ to client to/ from server, where: инересненько с переменными PUBLIC\PRIVATE пробовал ? cExpr := Leto_VarExprCreate( "Memvar1 [, MemvarX ]", @aArr ) Repeatedly call after each occasion of changed value: Leto_VarExprSync( aArr ) Clean up with: Leto_VarExprClear( aArr ) LETO_VAREXPRTEST( cExpression ) ==> lContainMemvar Return TRUE ( .T. ) if <cExpression> contains memvars LETO_VAREXPRCREATE( cExpression [, @aLetoVar ] ) ==> cModifiedExpression ... [/pre2] если объявленное в readme.txt работает, то есть движение вперед

Sergy: До серверных процедур (RPC, UDF) и обмена переменными пока не дошел. Пока изучаю возможности фонового взаимодействия с LetoDB сервером в отдельных потоках. В данный момент - не очень понимаю, что принципиально мешает передавать от клиента на сервер массивы и хэши. Пусть даже в текстовом виде. "Наверху" пусть примут строку, преобразуют ее один раз в переменную, а дальше - у меня много таких вот конструкций: SET FILTER TO table->client $ hListSelectedClients Работает шустро, удобно и понятно: в хэш можно выбрать хоть одного клиента, хоть сотню - на производительности это почти никак не скажется. ASCAN() по массиву выбранных клиентов тормозит: чем больше клиентов, тем сильнее. Сам бы подкинул Рольфу идею, но похоже, что я ему уже мозг вынес и он поставил меня в игнор...

SergKis: 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 в "форк" не знаю, можно ли так делать ?

SergKis: посмотрел "форк" на наличие HB_FUNC[pre2] 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: leto_ParseRecords( leto_Udf('UDF_dbEval', <xScope>, <xScopeBottom>, <xOrder>, <cFilter>, <lDeleted> ) ) while ! eof() ... skip enddo dbInfo( DBI_CLEARBUFFER ) удобен [/pre2]

SergKis: Sergy пишет у меня много таких вот конструкций: SET FILTER TO table->client $ hListSelectedClients можно заменить на cKli := ',' while !eof() cKli += alltrim(table->client)+',' skip end потом SET FILTER TO &( ','+table->client+',' $ '"'+cKli+'"' )

Sergy: SergKis пишет: массивы передаются, как параметры в 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 - но честно говоря, почти ничего не понял из вышесказанного, сорри Что за SkipBuffer и зачем в него класть aRecNo ?

Sergy: SergKis пишет: посмотрел "форк" на наличие HB_FUNC LETO_PARSEREC( cRecBuf ) --> nil LETO_PARSERECODRS( cRecBuf ) --> nil не увидел, не есть хорошо режим работы [pre2] * UDF_dbEval function returns buffer with records by order <xOrder>, and for condition, * defined in <xScope>, <xScopeBottom>, <cFilter>, <lDeleted> parameters * Function call from client: leto_ParseRecords( leto_Udf('UDF_dbEval', <xScope>, <xScopeBottom>, <xOrder>, <cFilter>, <lDeleted> ) ) while ! eof() ... skip enddo dbInfo( DBI_CLEARBUFFER ) [/pre2] удобен Не пользовался, но в readme.txt есть пометка: see new LETO_DBEVAL() as powerful alternative, or sample of UDF_dbEval() in tests/letoudf.prg

Sergy: SergKis пишет: можно заменить на cKli := ',' while !eof() cKli += alltrim(table->client)+',' skip end потом SET FILTER TO &( ','+table->client+',' $ '"'+cKli+'"' ) В паре мест делаю примерно похожим образом, но это 'костыли': чем длиннее строка, тем медленнее она будет обрабатываться. Хотелось-бы нормальное решение.

Dima: Sergy пишет: Хотелось-бы нормальное решение. В Leto Павел делал BM фильтры , не подходит ?

Sergy: Dima пишет: В Leto Павел делал BM фильтры , не подходит ? Поддержка rushmore bitmap фильтров в форке никуда не делась и для многократной выборки - вполне: сначала выбрать нужные записи, потом уже по ним работать. Например в DbEdit(). А для единичного отчета, когда Пете нужен отчет по всем его клиентам из Пензы за текущий квартал, а Васе - из Уфы за предыдущий год, то получается у каждого - минимум два прохода по таблице продаж. Причем первый из них - так и не оптимизирован хэшем, а второй - вообще лишний.

SergKis: Sergy пишетпочти ничего не понял из вышесказанного 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, по мну, БАААЛЬШОЙ минус, не смертельный, но ...

Sergy: Более-менее понятно, но очень уж зависимо от конкретной реализации. Пытаюсь по максимуму использовать стандартизованный интерфейс харборовских RDD и не использовать без особой необходимости Leto_xxx(). Ведь если через пару-тройку лет появится какой-нибудь WinterDB - опять всё переписывать? У меня половина юзеров работает с базой удаленно, через RDP. Для них - данные локальны и выборка идет через dbfntx. Другя половина - как обычно, через сеть. Сначала полностью через dbfntx, сейчас - без особой спешки все больше и больше операций переключаю на LetoDBf. Форк интересен (для меня) тем, что открыв таблицу один раз VIA "dbfntx" (rdp-локально) или VIA "leto" (по сети) я больше ничего не модифицирую в коде. Максимум - оптимизирую фильтр для выполнения на сервере. Но он точно так же будет работать как с leto, так и с dbfntx. Пока оставил на "сладкое" некоторые моменты в программе, которые нельзя решить "влоб": как например, не могу отказаться от простого фильтра по хэшу или фильтра, реагирующего на нажатия кнопок на клавиатуре (живой поиск), например: SET FILTER TO MyFilter() Да, сделав специфические заточки под Leto, я потеряю совместимость с dbfntx. Пока непонятно - будет ли стоить овчинка выделки. Проект большой и раскорячить его мне не хотелось бы. В данный момент у меня даже сетевые юзеры при отсутствии коннекта с LetoDB получат сообщение при входе, но продолжат работать, как ни в чем ни бывало. Чуть медленнее, чем "обычно" (к которому они уже привыкли), но работать. Понятно, что и локальных rdp-юзеров можно переключить полностью на локальный Leto сервер. И более того - с точки зрения целостности данных так и сделаю. Но процесс перевода небыстр, а для юзеров нужны всё новые и новые фичи, а не только ускорение старых. Такие вот мысли...

SergKis: Sergy пишет опять всё переписывать? можно исп. обертки функции\классы\препроцессор(leto_file.ch в "форк") If RddName() == 'LETO' ... Else ... EndIf исп. leto_sum(...), leto_group(...), можно пробнуть адаптировать их под работу без сервера или Пашу попросить это сделать, если очень надо. Но процесс перевода небыстр, а для юзеров нужны всё новые и новые фичи, а не только ускорение старых. На новые фичи перейти мне удалось только внедрением letodb с новой организацией базы на cdx (старая ntx), т.е. загрузка на сервер старой и организация переноса изменений (записи таблиц) с новой базы в старую. Новые фичи работают на letodb, старые как было, постепенно старые режимы переключаю на letodb

Sergy: SergKis пишет: На новые фичи перейти мне удалось только внедрением letodb Новые фичи - я имел в виду новые функции программы, никак не связанные со структурой/обработкой базы данных. Например, штрихкодирование документов, товаров, новые виды отчетов и тд и тп. с новой организацией базы на cdx (старая ntx) Вот это уже интересно для меня. Что такого принципиально интересного есть в cdx, чего нет в ntx ?

SergKis: Sergy пишет Что такого принципиально интересного есть в cdx, чего нет в ntx ? наличие тегов в одном индексном файле + SET AUTOPEN ON - подключение индекса автоматом, результат - целостность базы. INDEX ON DtoS(DATADOK)+CLIENT+VID_OPER+TIME_DOK TAG PERIOD INDEX ON CLIENT+DtoS(DATADOK)+VID_OPER+TIME_DOK TAG CLIENT И Ваш SET FILTER _FIELD->CLIENT $ hList... превращается в простые быстрые операции SET SCOPE ... OrdSetFocus('CLIENT') FOR i := 1 TO Len(hList...) cKli := ... SET SCOPE TO cKli, cKli // или leto_SetEnv(cKli, cKli,...) GO TOP // TBROWSE или что то еще ... тоже за период d1, d2 OrdSetFocus('PERIOD') FOR i := 1 TO Len(hList...) cKli := ... SET SCOPE TO DtoS(d1)+cKli, DtoS(d2)+cKli // или leto_SetEnv(DtoS(d1)+cKli,DtoS(d2)+ cKli,...) GO TOP // TBROWSE или что то еще ...

Dima: Таки да Scope или же BM фильтр работает очень шустро.

Sergy: Dima пишет: Таки да Scope или же BM фильтр работает очень шустро. SET SCOPE работает еще со времен Clipper, с DBFNTX - точно так-же, как и DBFCDX. Это базовые функции любой Харборовской RDD. Тоже самое и с bitmap фильтрами. AUTOPEN ... да, наверное это удобно - вместо [pre2]USE table INDEX table NEW[/pre2] писать [pre2]USE table NEW[/pre2]. Экономия - целых два слова в одной команде. )) Кстати, тут недавно наш админ поковырялся с 1с - там оказывается, народ извращается с этим монстром так: выносит индексные и прочие временные файлы на RAM-диск. Почему-то был уверен, что RAM-диски канули в лету вместе с DOS 6.22 - ан нет, в 2018 году этот метод ещё жив. Родилась идея - засунуть *.dbf файлы на шустрый SSD, а индексы - размещать на серверном RAM диске (чтобы флэш-память не изнашивалась понапрасну). Придется поковырять синтаксис команд USE ... INDEX ... - но мне кажется, овчинка будет стоить выделки. Может кто-то уже пробовал? SET AOUTOPEN тут точно не поможет...

Sergy: SergKis пишет: наличие тегов в одном индексном файле + SET AUTOPEN ON - подключение индекса автоматом, результат - целостность базы. Не очень понимаю - какая принципиальная разница - одна таблица и 2-3 индекса рядом с ней или одна таблица и один индексный файл. Кроме этого, в некоторых случаях (выборка по всей таблице с какими-нибудь экзотическими условиями, для которых нет смысла держать отдельный ntx/cdx) индекс может быть совсем не нужен - SKIP по нему будет выполняться гоооооорааааааздо медленнее, чем по dbf таблице без индекса. SergKis пишет: На новые фичи перейти мне удалось только внедрением letodb с новой организацией базы на cdx (старая ntx) Так и осталось непонятным, что именно ПРИНЦИПИАЛЬНО есть в CDX, чего нет в NTX ? Читал наших гуру (Viktor & Przemek) - они писали, что никаких сколь нибудь существенных отличий (для программиста) между DBFCDX и DBFNTX в реализации Harbour нет. CDX занимает меньше места на диске (легкая компрессия дерева), но требует чуть больше памяти (для того, чтобы развернуть сжатые данные). В общем и целом - тож на тож выходит.

PSP: Существенный прирост при использовании letodb можно получить, если использовать udf-функции, выполняемые на сервере. Все операции с базой выполняются локально на сервере. На клиент возвращается только результат.

Andrey: PSP пишет: Существенный прирост при использовании letodb можно получить, если использовать udf-функции, Да, это точно, подтверждаю. Можно посмотреть пример http://abonent4.ru/letodb/

SergKis: Sergy пишет SET SCOPE работает еще со времен Clipper обеспечивалось сторонними либами Comix, SixNsx,... индекс может быть совсем не нужен - SKIP по нему будет выполняться гоооооорааааааздо медленнее, чем по dbf таблице без индекса. Достаточно одной таблетки SET ORDER TO 0 и ок! letodb так, по умолчанию, делает при открытии таблица (AUTOPEN ON) и не важно кто и сколько раз ее открыл, нужны или нет тэги для работы, главное, модификация записей по части ключей, не сломает базу, с ntx надо правильно подключать все файлы, что бы не разрушить базу. одна таблица и 2-3 индекса рядом с ней или одна таблица и один индексный файл. это у справочников так, у инф. таблиц больше, на SixNsx есть 44 тега (работает оч.давно и очень надежно), а как если бы они были отдельными файлами ... "Чисто гтпотетичечки, ... теоретически ...", к примеру ваш CLIENT в таблице, добавим 2а тега INDEX ON CLIENT ... // список клиентов с информацией INDEX ON CLIENT ... UNIQUE // реал. список клиентов в информации если есть код территории еще INDEX ON KODTER ... UNIQUE // реал. список территорий в информации INDEX ON KODTER+CLIENT ... UNIQUE // реал. список клиентов по территория в информации INDEX ON KODTER+CLIENT ... // список всех территорий по клиентам в информации теперь можем без отборов давать 1.уник. список клиентов в tbrowse, ставить галочки и отработать выборку по галачкам по тэгу не уник. клиентов. 2. уник. список территорий в tbrowse, ставить галочки, по ним собрать для tbrowse (уник. KODTER+CLIENT) список клиентов, поставить галочки и отработать их по не уник. тэгу KODTER+CLIENT добавленные тэги не мешают жить таблице, но позволяют быстро работать при выборках. Если это отдельными ntx файлами, да в разных модулях\проектах - головная боль будет, при обеспечении надежности\целосности. между DBFCDX и DBFNTX в реализации Harbour нет и очень хорошо.

Pasha: Я бегло смотрел LetoDBf, когда он только появился, но до сих пор руки не доходили даже до простейшего теста. Наконец попробовал. Установил новую службу, благо название у нее отличается от старой. Если обе службы слушают один порт (2812), то они мешают друг другу, что понятно. Надо их запускать по очереди. Сделал простейший тест производительности, не самодостаточный, взял пару файлов master-detail, размером 2M и 8M. Запустил локально. Тест такой: Function Main( cPath ) Local i := 0, nSec Field CF1, CF2 REQUEST LETO RDDSETDEFAULT( "LETO" ) SET DATE FORMAT "dd/mm/yy" IF Empty( cPath ) cPath := "//127.0.0.1:2812/temp/" ELSE cPath := "//" + cPath + Iif( Right(cPath,1) $ "/\", "", "/" ) ENDIF ? leto_Connect(cPath) use f1 new index f1 use f2 new index f2 nSec := Seconds() select f1 go top while ! eof() select f2 dbSeek(f1->CF1+f1->CF2) while ! eof() .and. CF1 = f1->CF1 .and. CF2 = f1->CF2 i ++ skip enddo select f1 skip enddo ? Seconds() - nSec, i close all leto_disconnect() Return Nil Старый (оригинальный, "наш") letodb управился за 31 сек. Кстати, обратите внимание, команде use не надо указывать никаких дополнительных строк коннекта. Это было необходимо в ранних версиях. LetoDBf не смог открыть файлы в папке temp, хотя путь есть в строке коннекта. Но это думается мелочь, которую можно подработать в процессе. Зато тест он прогнал за 18 сек, что впечатляет. Другие тесты пока не гонял, и разницу в функциональности не исследовал. В любом случае можно сказать, что Рольф проделал не просто большую, а огромную работу, и проект интересный. Дима, кажется ты не стал использовать letodb из-за отсутствия серверных relations, которые есть в ads. Рольф судя по доке их сделал (я не пробовал), но только в режиме No_Save_WA = 1.

SergKis: Pasha пишет Рольф проделал не просто большую, а огромную работу, и проект интересный. README.md LetoDBf client lib ( used by your application ) became fully MultiThread save, each thread opens its own connection to the server. Threads, threads threads .., wherever you look. т.е. клиентская библиотека полностью MultiThread

Петр: Sergy пишет: Так и осталось непонятным, что именно ПРИНЦИПИАЛЬНО есть в CDX, чего нет в NTX ? Ну так и почитайте документацию (xhb-diff.txt). И multitag indexes, если что, NTX поддерживает.

Pasha: Обратил внимание, что тест для letodb отработал без No_Save_WA = 1, а для letodbf - с этой настройкой. Попытался запустить тест для letodbf без No_Save_WA = 1. Неудача: похоже файлы открыты без индекса, ошибка: workarea not indexed То есть шероховатости конечно имеются.

Sergy: Pasha пишет: LetoDBf не смог открыть файлы в папке temp, хотя путь есть в строке коннекта. Но это думается мелочь, которую можно подработать в процессе. Зато тест он прогнал за 18 сек, что впечатляет. Не вижу в тексте Leto_ToggleZip(1) - попробуй пожалуйста - интересно посмотреть на твой результат. Функция должна стоять сразу после Leto_Connect()

Sergy: SergKis пишет: т.е. клиентская библиотека полностью MultiThread С недавних пор MT для клиента LetoDBf можно отключить при сборке библиотеки. Не знаю, кому это может понадобиться, но при этом теряется одно из интересных преимуществ - "двухканальность", когда по одному порту идут сплошным потоком данные, а по соседнему - только редкая инфа об ошибках в пакетах.

Pasha: С Leto_ToggleZip(1) получилось резкое падение производительности: запустил 10 минут назад, и все жжжжжжжжддддддуууууууууууууу. Прошло 15 минут - надоело, нажал alt+c, ctrl+c - нифига, задача висит. Пришлось его кувалдой таскменеджером Впрочем, для локального теста так и должно быть, но не так же сильно. Так что я не стал бы использовать эту фичу. В letodb (я так буду называть оригинальный проект, в отличие от letodbf) тоже есть сжатие пакетов, но в своей работе я его тоже не использовал.

Sergy: Pasha пишет: Обратил внимание, что тест для letodb отработал без No_Save_WA = 1, а для letodbf - с этой настройкой. Паша, поясни пожалуйста, зачем самый "первый" режим (когда No_Save_WA = 0) нужен. Я правильно понимаю, что сервер открывает таблицу, делает по ней выборку и вместо того, чтобы закрыть её - отправляет в отстойник. Если она кому-то понадобится - сервер забирает ее из detached area и использует вновь. Но ведь в этот момент никто не может открыть эту таблицу? Какой вообще в этом смысл, если изначально планируется, что с базой работает более чем один юзер? Пока один делает выборку - другому предлагается пойти покурить? Вряд-ли - скорее всего, я что-то крупно не понял в англицком... Жесткий оффтопик: И ещё - вымораживает, когда программисты (сам когда-то был таким) пишут в диалогах: "Не использовать выборку тра-ля-ля [v]". Т.е. положительный ответ (галка) несет отрицательный результат (не использовать). Ппц... Напомнило - "Вы не придете сегодня на работу ?" - "Да. Не приду". Или "Нет. Не приду". Тоже самое - с No_Save_WA=1. Да. Единица. Не использовать сохранение рабочих областей.

Sergy: Pasha пишет: С Leto_ToggleZip(1) получилось резкое падение производительности: запустил 10 минут назад, и все жжжжжжжжддддддуууууууууууууу. Чет явно не то... Может Leto_ToogleZip(1) включил, а сервак был запущен - оригинальный, не форк ?

Pasha: Там же протокол совсем разный. Если перепутать клиент и сервер, то программа сразу свалится.

PSP: Sergy пишет: Паша, поясни пожалуйста, зачем самый "первый" режим (когда No_Save_WA = 0) нужен. Да, мне тоже интересны подробности работы этой настройки. Паша, если можно, просвети.

Sergy: SergKis пишет: INDEX ON KODTER ... UNIQUE // реал. список территорий в информации INDEX ON KODTER+CLIENT ... UNIQUE // реал. список клиентов по территория в информации INDEX ON KODTER+CLIENT ... // список всех территорий по клиентам в информации Не очень понятен смысл примера, в DBFNTX работает так: Если есть INDEX ON KODTER+CLIENT TO ... То SEEK cKodTer (без уточнения клиента) даст первое вхождение нужного KODTER. Можно указать SEEK cKodTer+cClient - тогда поиск будет по территории + нужному клиенту. Всё это - по одному индексу. Т.е. другими словами, вместо индексов, частично использующих одни и те-же данные: INDEX ON DTOS(table->date)+STR(table->seller,5)+STR(table->buyer,5)+STR(table->invoice) TO table1 INDEX ON DTOS(table->date)+STR(table->seller,5)+STR(table->buyer,5) TO table2 INDEX ON DTOS(table->date)+STR(table->seller,5) TO table3 INDEX ON DTOS(table->date) TO table4 Можно построить только один, первый. И все "неполные" выборки по нему будут работать норм: как выборка на дату, так и выборка по дате+продавцу, так и выборка по дате+продавцу+клиенту. Вот если нужно быстро найти все накладные покупателя - тогда да, нужен отдельный индекс, тк "перепрыгнуть" в выражении через "продавца" не выйдет. Думаю, что и DBFCDX так может. Поэтому мне не очень понятен смысл в 44 тегах/индексах... Для 99% моих ситуаций хватает одного-двух. Совсем недавно столкнулся с возможной (?) необходимостью в третьем индексе для одной из таблиц, но знакомство с LetoDBf дает возможность подумать - может и обойдусь без индекса, раз данные будут шустро выбираться на сервере...

Pasha: Почитайте эту старую тему: http://clipper.borda.ru/?1-7-0-00000037-000-0-0-1486290536 В режиме No_Save_WA = 0, а этот режим является основным для letodb, сервер не открывает для каждого клиента одну и ту же таблицу, а использует одну и туже pArea, если таблица уже открыта. Соответственно сервер делает не физическую блокировку записи ли таблицы, а логическую. Для No_Save_WA = 1 для каждой команды use с клиента сервер открывает свою копию рабочей области, и соответственно выполняет уже физические блокировки. Этот режим считался в letodb экспериментальным, я сам его никогда не использовал, и не скажу, допилил ли его Александр в конце концов. В letodbf похоже основным сделан режим No_Save_WA = 1. Но точно я не скажу. Так что для меня вопрос: "зачем самый "первый" режим (когда No_Save_WA = 0) нужен" стоит как бы наоборот: "зачем нужен второй ?".

PSP: Pasha пишет: Почитайте эту старую тему: Я читал ее. Но она как-то затухла. Поэтому остались вопросы.

Pasha: В конце концов, смотрим доку: LetoDB: No_Save_WA = 0 - Когда этот режим установлен, при каждом вызове dbUseArea() сервер действительно открывает файл и создает новую рабочую область (workarea) - в режиме по умолчанию каждый файл открывается сервером только один раз и образуется одна реальная рабочая область для этого файла для всех клиентов. Теоретически это режим может увеличить производительность при большом количестве активных клиентов, но он недостаточно тестировался, поэтому для рабочих приложений рекомендуется использовать режим по умолчанию. LetoDBf: No_Save_WA = 1 - server mode of internally handling database tables 1 each dbUseArea() will cause a real file open operation by the OS, identical to what client requested, so workareas at the server are same as at client side. [ WA number, alias, filter conditions, relations ] 0 each table is opened only one time, this workarea 'exchanged' in between client requests. so only one connection will have access to the table at a time. No relations active at server, Alias names at server are different from the client. Recommend '1' if you plan to execute functions at server side ( UDF ).

SergKis: Sergy пишет Не очень понятен смысл примера... Смысл в том, что введенные unique тэги уже являются готовой выборкой, переключив та тэг INDEX ON KODTER ... UNIQUE // реал. список территорий в информации (в справочнике может быть стопятсот тыс. позиций) SetOrdFocus('KODTR') GO TOP tbrowse(...) // связав со справочником территорий и взяв наименование и ... имеем список реальных территорий, где можем поставить галочку\выбрать - для получения списка клиентов этой территории, т.е. INDEX ON KODTER+CLIENT ... UNIQUE // реал. список клиентов на выбранной территории SetOrdFocus('KODTER_CLI') SET SCOPE TO cKodTer, cKodTer GO TOP tbrowse(...) // реал. список клиентов территории, связав со справочником клиентов берем наименование и ... отмечаем галками\выбором нужных клиентов и у нас готовы параметры запроса для тэга INDEX ON KODTER+CLIENT ... // список всех территорий по клиентам в информации вот тут и выполняем запрос по установкам выше. территория+клиенты, помеченные галкой. период я отбросил для упрощения примера. Поэтому мне не очень понятен смысл в 44 тегах/индексах Смысл в том, что нет выборок типа select * from ..., а простыми переключениями тэгов отображаются таблицы в разных разрезах

PSP: Pasha пишет: В конце концов, смотрим доку: LetoDB: No_Save_WA = 0 - Когда этот режим установлен, при каждом вызове dbUseArea() сервер действительно открывает файл и создает новую рабочую область (workarea) - в режиме по умолчанию каждый файл открывается сервером только один раз и образуется одна реальная рабочая область для этого файла для всех клиентов. Сто раз смотрел)) Формулировка уж какая-то нечёткая) Совпадает ли поведение letodb и letodbf при одинаковых установках этого параметра? Как 0 или 1 отражается на выполнении udf? (это "Recommend '1' if you plan to execute functions at server side ( UDF )" применимо к оригиналу?) Нет ли возможности выяснить, допилил ли Александр это?

Sergy: Pasha пишет: В конце концов, смотрим доку: LetoDB: No_Save_WA = 0 - Когда этот режим установлен, при каждом вызове dbUseArea() сервер действительно открывает файл и создает новую рабочую область (workarea) - в режиме по умолчанию каждый файл открывается сервером только один раз и образуется одна реальная рабочая область для этого файла для всех клиентов. Теоретически это режим может увеличить производительность при большом количестве активных клиентов, но он недостаточно тестировался, поэтому для рабочих приложений рекомендуется использовать режим по умолчанию. Сорри, для непонятливых: Для LetoDb (не форк): "Когда режим установлен": No_Save_WA = 1 ?? "Режим по умолчанию": No_Save_WA = 0 ? Для обоих версий: Если No_Save_WA = 0 - в каждый момент времени таблицей может пользоваться только один клиент: so only one connection will have access to the table at a time ??? Зачем такой режим нужен в принципе? Ведь заявлено, что "теоретически может дать прирост производительности при большом количестве клиентов". Получается, что-то типа USE table EXCLUSIVE ? Что-то скорее всего, я не догоняю...

SergKis: Вроде так было: No_Save_WA = 0 - идет оптимизация открытий таблиц, одна таблица, открытая несколькими клиентами открывается один раз в области WA при No_Save_WA = 1 каждый запрос на открытие любой таблицы получает новую область WA А.Кресин занимался доводкой этого режима, кто то сильно тестировал и был положительный результат

PSP: Только Александр Кресин может ответить, видимо...

Sergy: SergKis пишет: Смысл в том, что нет выборок типа select * from ..., а простыми переключениями тэгов отображаются таблицы в разных разрезах Сергей, спасибо за разъяснения. Никогда не пользовался UNIQUE индексами, нужно будет посмотреть на них гораздо пристальнее.

Pasha: PSP пишет: Только Александр Кресин может ответить, видимо... Ну давайте еще раз разжуем режим No_Save_WA. Пусть два клиента открывают одну и ту же таблицу. Как это делается в режиме No_Save_WA = 0 Поток для 1-го клиента выполняет команду use в монопольном режиме. Поток для 2-го не делает use, а разделяет уже открытую таблицу с 1-м потоком. Причем запросы к таблице от обеих клиентов выполняются без задержки, одновременно. В режиме No_Save_WA = 1 поток для 1-го клиента выполняет команду use shared. Поток для 2-го - аналогично, т.е на сервере эта таблица открыта два (несколько) раз. Для letodb 1-й режим - основной, в работе оба режима отличаются способом блокировки: в 1-м выполняется логическая виртуальная блокировка, то есть создаются списки блокированных записей, во втором - блокировка средствами rdd. Ну и во втором режиме поскольку таблицы открыты как shared, их может открыть стороннее приложение. В letodbf основным является 2-й режим, ну и в нем судя по всему реализованы еще дополнительные возможности, я уже упоминал серверные relations. Какой режим лучше - надо тестировать. Я этим не занимался. Ну и вопрос "а нахрена вообще такой-то режим" наверное задавать не стоит. Для нас "мамы всякие нужны, мамы всякие важны". Наверное что-то лучше будет работать в первом режиме, а что-то во втором.

Pasha: Sergy пишет: Сергей, спасибо за разъяснения. Никогда не пользовался UNIQUE индексами, нужно будет посмотреть на них гораздо пристальнее. А есть еще индексы с условием for, тоже удобная штука. Поддерживаются и в ntx, и в cdx

Pasha: Sergy пишет: Не вижу в тексте Leto_ToggleZip(1) Не работает совсем этот Leto_ToggleZip. Тест зависает на первом use В логе найдено: 02.13.2018 10:39:57 INFO: connected 127.0.0.1:51141 test_db.exe CP: EN DF: dd/mm/yy conn-ID 0 02.13.2018 10:40:01 ERROR thread2() leto_SockRec != ulRecvLen 02.13.2018 10:40:01 INFO: disconnect 127.0.0.1:51141 test_db.exe users=(1 : 1 : 1), tables=(0 : 0) Сервер собран с harbour 3.2 + mingw64 Клиент harbour 3.2 + bcc55 letodbf скачивал вчера

PSP: Pasha пишет: Ну давайте еще раз разжуем режим No_Save_WA. Спасибо за разъяснения. Остался один вопрос: доделал ли Кресин режим "1".

Pasha: Pasha пишет: Не работает совсем этот Leto_ToggleZip. Тест зависает на первом use Ага, нашел, где собака порылась: readme.txt BCC55 and maybe also newer ones have a problem with compiling LZ4 compression library, you will get this case slower ZLib compression. This must fit together for client lib and server when you want to use network traffic compression. It is configured by this "{!bcc}" at top in the ".hbp" files.

SergKis: Pasha пишет Ну и во втором режиме поскольку таблицы открыты как shared, их может открыть стороннее приложение. С No_Save_WA = 0 получить режим shared для таблиц можно, поставив в ini Share_Tables = 1, т.е. таблицы может открывать стороннее приложение. А есть еще индексы с условием for, Замечание правильное. В индексных выражениях условие FOR я подразумевал обязательным, т.е. INDEX ON KODTER ... FOR ! Deleted() UNIQUE ... работа с инф. тэга не зависит от режима SET DELETAD ON\OFF подключенного модуля. Что еще добавлять в FOR это уже по ситуации. Наличие тега для доступа к удаленным записям INDEX ON ID ... FOR Deleted() считаю обязательным в таблицах задач.

Pasha: Прогнал тест по сети. LetoDB (клиент bcc, не пересобирал) - 184 сек LetoDBf + mingw32: несжатые пакеты 121 сек сжатые пакеты 110 сек

PSP: Это большая разница.

Pasha: SergKis пишет: Наличие тега для доступа к удаленным записям INDEX ON ID ... FOR Deleted() считаю обязательным в таблицах задач. Я при удалении записи полностью ее очищаю. Так что удаленные записи оказываются по индексу всегда "сверху". И обхожусь без дополнительного тега.

SergKis: Pasha пишет И обхожусь без дополнительного тега. В записи есть поля ID_DTM_ADD, ID_DTM_MOD, ID_DTM_DEL отражающие TimeStamp (C или @ типов), запись никогда не очищается (длительность действия год) как и идентификатор записи - все это государственно отчетные показатели. Организация тэгов, как показано выше, позволила упростить запросы по отчетности (лог журналы тоже есть - они уже для доп. разбирательств). Да и для внутр. исп. тэг по удаленным оказался полезным, начальнику любого уровня глянуть на browse что где наколбасили.

Pasha: SergKis пишет: В записи есть поля ID_DTM_ADD, ID_DTM_MOD, ID_DTM_DEL отражающие TimeStamp (C или @ типов), запись никогда не очищается Это же неиндескные поля, для индекса их значение до лампочки. Может у вас задача другая ? У меня если запись удалена, она просто используется для добавления новых из пула удаленных. Схема простая: go top, если первая запись удалена и индексное выражение пустое - она используется, если нет - то dbAppend

SergKis: PS letodb с триггером, оказался ОЧЕНЬ полезным для решения этих задач.

SergKis: Pasha пишет У меня если запись удалена, она просто используется для добавления новых из пула удаленных Так очищать запись нельзя и повторно использовать тоже. Если так делать, надо городить огород на текстовые логи для добывания инф. для проверяющих организаций. Скажу так, ВСЕ клиенты не хотят проверяющим давать инф. из этих логов, у проверяющих есть начальные данные (их системы) и им надо дать конечные, без промежуточных

SergKis: PS Выделенный на запись ID не должен повторно использоваться в течении года. Модификация по ID записи возможна, отражается в ID_DTM_MOD и user кто делал, при удалении запись "замораживается"

Sergy: Pasha пишет: Ну давайте еще раз разжуем режим No_Save_WA. Пусть два клиента открывают одну и ту же таблицу. Как это делается в режиме No_Save_WA = 0 Поток для 1-го клиента выполняет команду use в монопольном режиме. Поток для 2-го не делает use, а разделяет уже открытую таблицу с 1-м потоком. Причем запросы к таблице от обеих клиентов выполняются без задержки, одновременно. В режиме No_Save_WA = 1 поток для 1-го клиента выполняет команду use shared. Поток для 2-го - аналогично, т.е на сервере эта таблица открыта два (несколько) раз. Для letodb 1-й режим - основной, в работе оба режима отличаются способом блокировки: в 1-м выполняется логическая виртуальная блокировка, то есть создаются списки блокированных записей, во втором - блокировка средствами rdd. Ну и во втором режиме поскольку таблицы открыты как shared, их может открыть стороннее приложение. В letodbf основным является 2-й режим, ну и в нем судя по всему реализованы еще дополнительные возможности, я уже упоминал серверные relations. Какой режим лучше - надо тестировать. Я этим не занимался. Ну и вопрос "а нахрена вообще такой-то режим" наверное задавать не стоит. Для нас "мамы всякие нужны, мамы всякие важны". Наверное что-то лучше будет работать в первом режиме, а что-то во втором. Спасибо за разъяснения. Очень прошу не расценивать мои слова "а зачем такой режим вообще нужен" как персональное оскорбление. Просто у меня вызвала недоумение цитата Рольфа: so only one connection will have access to the table at a time Поэтому и не мог понять, в чем смысл. Нужно будет попробовать потестировать. Жаль, что в данном режиме не поддерживается прямое указание алиасов для полей таблиц. У меня с давних времен все компилируется с опцией -m, поэтому все поля во всех выражения, в тч фильтрах и relation всегда идут только с алиасом.

SergKis: Sergy пишет У меня с давних времен все компилируется с опцией -m, поэтому все поля во всех выражения, в тч фильтрах и relation всегда идут только с алиасом. В чем "выгода" от применения алиаса для тек. обл. в уст. filtra ? Со времен S87 с -m этого не делаю, в 5.2 какое то время писал определения FIELD ..., потом "забыл" и _FIELD->... так же не использую - все прекрасно понимается, для переменных памяти исп. венгерскую запись. Для однообразного кода (это на любителя) отказался и от SET RELATION, перейдя на подводы с анализом - неудобств в связи с этим не испытал, а иногда и выигрывал, когда с течением времени, надо было менять алгоритм подвода, не нашли тут, делаем др. подводы или ищем в др. алиасах. Когда много критериев для отбора (сложный фильтр), использую массив блоков кода для каждого критерия и если "условно" все элементы, выполненные по Eval, дадут .T. => запись в отборе.

Pasha: SergKis пишет: Так очищать запись нельзя и повторно использовать тоже. Если так делать, надо городить огород на текстовые логи для добывания инф. для проверяющих организаций. Скажу так, ВСЕ клиенты не хотят проверяющим давать инф. из этих логов, у проверяющих есть начальные данные (их системы) и им надо дать конечные, без промежуточных У вас проверяющие в базах копаются, что ли ? Жесть.

SergKis: Pasha пишет У вас проверяющие в базах копаются, что ли ? Жесть. Сами не лезут, просят выгрузить в excel то то и то то, с ID, ID_DTM_ADD, ID_DTM_MOD, ID_DTM_DEL + ID_DTM_ANULIR (анулированные докум.). Ежемесячно для них форм. xml файлы для загрузки в eds систему (по налогам контролируют), маленькие фирмы вводят руками документы в эту систему, к примеру, через хром.

SergKis: PS Когда просят выгрузить, в лучшем случае сидят в соседней комнате или стоят над "душой", т.е. формировать надо быстро.

SergKis: PPS Раньше проявлять сделки с клиентом, надо было при превышении 1000 eur за месяц, сейчас со 150 eur.

Pasha: До чего дошел прогресс :)

ZAlex: SergKis пишет Модификация по ID записи возможна, отражается в ID_DTM_MOD и user кто делал, при удалении запись "замораживается" А запись с пометкой удаленная? И делаете ли Вы когда-нибудь PACK?

SergKis: ZAlex пишет А запись с пометкой удаленная? И делаете ли Вы когда-нибудь PACK? Да, запись с пометкой удаленная и команда PACK не используется от слова никогда. Есть еще понятие свободная\занятая запись (осталась от времен S87), когда записи в таблицу добавляются страницей, к примеру по 300 (из ini установки берется) записей за раз. Но пока не определился оставлять этот механизм или нет для letodb.

Sergy: SergKis пишет: В чем "выгода" от применения алиаса для тек. обл. в уст. filtra ? Х.е.з., если честно. Это "сейчас" есть умный Харбор, а "тогда" нужна была четкая и полная уверенность в том, что переменная в каком-нибудь выражении не перепутается с именем поля. Поэтому для себя решил, что -m будет дисциплинировать и я не поленюсь добавлять к имени поля название алиаса. Со временем я оценил "лишний труд" и это дало определенные плоды: при модификации имен/типов/размеров полей простым текстовым поиском по исходникам можно легко найти и внести изменения в тех местах, которые могут затронуть эти изменения.

Pasha: PSP пишет: Это большая разница. Я случайно заметил, что в letodb.ini, который идет с LetoDBf, стоит параметр: Cache_Records = 21 Так что тест выполнялся в неравных условиях. Добавил в тест вызов leto_SetSkipBuffer(21) Собрал тест с letoDB + mingw32, чтобы условия были совсем уж равными. Результат по сети - 123 сек, что практически совпадает с тестом LetoDBf Тест для LetoDBf кстати собирал с флажком -mt, чтобы задействовать преимущество использования двух портов. Я даже несколько обескуражен результатом. Конечно, может быть новшества LetoDBf сыграют в каких-то других условиях, тест то больно простой.

PSP: Это хорошо) У меня программа с letodbf в некоторых местах падает с GPF 0xc0000005 ACCESS VIOLATION. Не могу найти причину. Пока нет времени разбираться. С оригинальным letodb всё нормально.

SergKis: Pasha пишет Конечно, может быть новшества LetoDBf сыграют в каких-то других условиях, тест то больно простой. Мои ощущения, letoDBf поведением приближен к стандартному rdd (+SIXCDX), добавлено поведение SBM сервера и попытка перенести public\private переменные (синхронизацией, кешированием) на сторону сервера, для использования в filtrах и т.д. Даст это прирост скорости или нет - это не первостепенный вопрос.

Pasha: SergKis пишет: попытка перенести public\private переменные (синхронизацией, кешированием) на сторону сервера, для использования в filtrах и т.д. Хоть я не разбирался, для синхронизации нужен принудительный вызов Leto_VarExprSync ? При этом все значения public/private передаются на сервер, что ли ? Не очень понятно, зачем это все нужно. Предположим, в строке для фильтра есть использование переменной памяти. При изменении значения переменной фильтр должен менять поведение, что ли ? Я избегаю использовать переменные памяти, предпочитаю local/static. Фильтры, которые после установки динамически изменялись бы, мне не нужны. Это какая-то экзотика. Сконструировать строку для фильтра, задав в ней нужные параметры, не составляет особой проблемы. И зачем городить этот огород ?

SergKis: Pasha пишет Сконструировать строку для фильтра, задав в ней нужные параметры, не составляет особой проблемы. И зачем городить этот огород ? Полностью согласен. С какой целю сделано, не ясно.

Pasha: Глянул Leto_VarExprSync. Есть массив синхронизируемых переменных. Клиент хранит для каждой значение предыдущей синхронизации. Если значение изменилось, на сервер передается команда LetoVarSet Если изменилось значение скажем 20-ти переменных - будет 20 команд. Как бы можно было бы обойтись одной, сделать что-то вроде LetoVarsSet.

SergKis: PSP пишет У меня программа с letodbf в некоторых местах падает с GPF 0xc0000005 ACCESS VIOLATION. Не могу найти причину Судя по Changelog.txt сборки пекутся как пирожки, т.е. сыровато еще состояние

PSP: SergKis пишет: Судя по Changelog.txt сборки пекутся как пирожки, т.е. сыровато еще состояние Согласен. Поэтому и не спешу искать)

Sergy: SergKis пишет: Судя по Changelog.txt сборки пекутся как пирожки, т.е. сыровато еще состояние Не собираюсь никого защищать, но с лета-осени 2017г никаких существенных для программиста изменений не было. Насколько мой английский мне позволяет понять ситуацию - Рольф закусился с Linux/SMB сервером: там обнаружился странный косяк: два приложения (сервер Leto и обычная программа на DBFCDX/DBFNTX) пытаются в монопольном режиме получить доступ к файлу и оба... успешно его получают. Судя по переписке в форуме - похоже что решил.

SergKis: Sergy пишет Не собираюсь никого защищать, но с лета-осени 2017г никаких существенных для программиста изменений не было. Так я и не нападаю, надо пробовать в тестовом режиме, если видны плюсы перехода. Для себя, пока, не вижу необходимости переходить, хотя режим No_Save_WA = 1 интересует в letoDB, но руки не доходят.

SergKis: Sergy пишет там обнаружился странный косяк: два приложения (сервер Leto и обычная программа на DBFCDX/DBFNTX) пытаются в монопольном режиме получить доступ к файлу и оба... успешно его получают Share_Tables = 1 для меня основной, т.к. letodb+httpserver+hb приложение работают на одну базу

Sergy: SergKis пишет: Share_Tables = 1 для меня основной, т.к. letodb+httpserver+hb приложение работают на одну базу Для меня тоже, но для одного кекса потребовалось одновременно запустить на одну базу две разных программы, время от времени требующим монопольный доступ. И если Leto+Leto работает, DBFxxx+DBFxxx работает, а вот Leto+DBFxxx - именно под Линуксом - не выходило. Это я к тому, что дело вовсе не в сырости версии форка как таковой: возникла специфическая задача, походу её решили и весьма нетривиальным способом.

Pasha: SergKis пишет: Share_Tables = 1 для меня основной, т.к. letodb+httpserver+hb приложение работают на одну базу Производительность без Share_Tables = 1 намного выше, чем с ним. Теряются преимущества работы с dbf в монопольном режиме, а они существенные. Если стороннему non-harbour приложению также надо иметь доступ к базам letodb, то это возможно организовать посредством ole-сервера, см. utils\olesrv Но это только для win-платформы

Sergy: Pasha пишет: Я даже несколько обескуражен результатом. Конечно, может быть новшества LetoDBf сыграют в каких-то других условиях, тест то больно простой. Интересно. Я тоже в одном месте был "обескуражен". Хотелось-бы понять: это особенность форка или в принципе, LetoDB. Рольф обиделся и в молчанку стал играть... Суть теста такова: открывается таблица без индекса и по ней делается большое количество GO INT(RAND(RECCOUNT())) [pre2]FUNC Main(cConnect, cDir, cFileName,nSkipBuffer) // IF Empty( cConnect ) .OR. (cConnect == ".") cConnect := "192.168.1.120:2812" ENDIF // IF Empty( cDir ) .OR. (cDir == ".") cDir := "w:\hb\data\" ENDIF // IF LEN(DIRECTORY(cDir+"*.*")) == 0 ? "Directory "+cDir+" is wrong" QUIT ENDIF // IF EMPTY(cFileName) .OR. (cFileName == ".") cFileName := "invcheck" ENDIF // IF EMPTY(nSkipBuffer) nSkipBuffer := 21 // by default ELSE nSkipBuffer := VAL(nSkipBuffer) IF EMPTY(nSkipBuffer) nSkipBuffer := 21 ENDIF ENDIF // ? "1) cConnect = ",cConnect ? "2) cDir = ",cDir ? "3) cFileName = ",cFileName ? "4) nSkipBuffer = ",hb_NTOS(nSkipBuffer) // IF FILE(cDir+cFileName+".dbf") ? "Table "+cDir+cFileName+".dbf is reachable locally. OK" ELSE ? "Table "+cDir+cFileName+".dbf is not reachable locally. QUIT" QUIT ENDIF // IF Leto_Connect(cConnect) >= 0 ? "LetoDBf - connect ok." Leto_ToggleZip(1) LETO_SETSKIPBUFFER( nSkipBuffer ) ELSE ? "LetoDbf - failed to connect. QUIT" QUIT ENDIF // IF Leto_File(cFileName+".dbf") LetoSpeedTest(cDir,cFileName,10000) ELSE ? "Leto_File('"+cFileName+".dbf') = FALSE" ENDIF ? ? "Press any key to exit" INKEY(0) // RETURN 0 * ---------------------------------------- * FUNC LetoSpeedTest(cWorkDir,cFileName,nSize) LOCAL nStart,nStop,i,aRecords // USE (cWorkDir+cFileName) INDEX (cWorkDir+cFileName) EXCLUSIVE NEW VIA "DBFNTX" IF NETERR() ? "DBFNTX - open error." RETURN .F. ELSE ? "Table "+cFileName+".dbf is opened, it has "+hb_NTOS(RECCOUNT())+" records,",; hb_NTOS(FileSize(cWorkDir+cFileName+".dbf") / (1024 * 1024))," MB" ? ENDIF // aRecords := ARRAY(nSize) FOR i:=1 TO nSize aRecords[ i ] := INT(RAND()*nSize) NEXT i // ? "Testing "+hb_NTOS(nSize)+" jumps via DBFNTX: " nStart := hb_Milliseconds() FOR i:=1 TO nSize GO aRecords[ i ] DoSomething() NEXT i nStop := hb_Milliseconds() // ?? STR(nStop-nStart,10),"ms, "+STR(nSize/(nStop-nStart)*1000,10,1)+" jumps/sec" CLOSE // USE (cFileName) INDEX (cFileName) EXCLUSIVE NEW VIA "LETO" IF NETERR() ? "LETO - open error." RETURN .F. ELSE ? "Testing "+hb_NTOS(nSize)+" jumps via LETO: " ENDIF nStart := hb_Milliseconds() FOR i:=1 TO nSize GO aRecords[ i ] DoSomething() NEXT i nStop := hb_Milliseconds() // ?? STR(nStop-nStart,10),"ms, "+STR(nSize/(nStop-nStart)*1000,10,1)+" jumps/sec" CLOSE // RETURN "done" * ----------------------------- * STATIC FUNC DoSomething() LOCAL x := FIELDGET(1),; y := FIELDGET(2) RETURN hb_ValToExp(x)+hb_ValToExp(y) * * * EOF is here...[/pre2] Компилировал вот так: letodb.hbc -W3 -es0 -n -mt hbct.hbc Будет несколько минут времени - прогони плиз его у себя на обоих вариантах: основном и форковском. Интересно сравнить. Может это LetoDB(f), а может у меня настройки какие-то кривые - все в общем летает, а тут - скорость в 40! раз ниже, чем у DBFNTX по сети.

Pasha: Я правильно понял, что этот тест LetoDBf прогоняет в 40 раз медленнее, чем LetoDB ? В любом случае, сразу можно сказать, что для GO INT(RAND(RECCOUNT())) skip buffer не помогает, а мешает. Вместо одной записи сервер передает клиенту 10, или что там в настройках. А они не нужны. Поэтому лучше перед такими goto задать leto_SetSkipBuffer(1). Ну а потом вернуть прежнее значение, потому что для обычных выборок эта настройка очень важна.

Sergy: Сорри, не в 40, а в 24 раза LetoDBf получается медленнее, чем DBFNTX по сети: [pre2] 1) cConnect = 192.168.1.120:2812 2) cDir = w:\hb\data\ 3) cFileName = invcheck 4) nSkipBuffer = 21 Table w:\hb\data\invcheck.dbf is reachable locally. OK LetoDBf - connect ok. Table invcheck.dbf is opened, it has 957046 records, 66.63 MB Testing 10000 jumps via DBFNTX: 221 ms, 45248.9 jumps/sec Testing 10000 jumps via LETO: 5347 ms, 1870.2 jumps/sec Press any key to exit[/pre2] SkipBuffer можно задать из командной строки, можно поправить в тексте программы. Менял и так и эдак - без особого прироста производительности.

Dima: Sergy пишет: DBFNTX по сети: А ежели не лень , сделай тест с CDX.

Sergy: Dima пишет: А ежели не лень , сделай тест с CDX. Индексов CDX у меня нет, от слова "совсем". Но делал без индексов, просто USE table EXCLUSIVE NEW - пофиг: в обоих случаях процентов на 10 быстрее, но в итоге: [pre2] Testing 10000 jumps via DBFNTX: 198 ms, 50505.1 jumps/sec (no index) Testing 10000 jumps via LETO: 5212 ms, 1918.6 jumps/sec (no index)[/pre2]

SergKis: Sergy Что покажет установка BM фильтра на массив RecNo() и обработка в цикле всех записей ?

Sergy: SergKis пишет: Что покажет установка BM фильтра на массив RecNo() и обработка в цикле всех записей ? Честно говоря, не знаю. У меня вопрос возник в одном, не слишком типичном месте: сначала идет выборка, собираются по некому сложному условию куча записей в массив RECNO(), а потом по ним идет DBEDIT(). После переключения данного фрагмента кода на LetoDBf юзеры (неожиданно), все как один, стали жаловаться на "тормоза": курсор переходил от записи к записи с задержкой около секунды. После того, как вернул работу на "стандартный" DBFNTX - тормоза исчезли, а я пытаюсь понять: что-же это такое могло быть. Перекомпилировать сервер под BMDBFNTX нет особого смысла: я уже изменил логику в этом месте программы полностью, отказавшись от массива RECNO() в пользу локальной таблицы с выбранными данными. Работает и с LetoDBf и DBFNTX без тормозов вообще. Просто остался чисто спортивный интерес: что это было - ошибка в форке или у меня что-то не так настроено.

SergKis: Sergy пишет я уже изменил логику в этом месте программы полностью, отказавшись от массива RECNO() в пользу локальной таблицы с выбранными данными. с изменением алгоритма все понятно, а с массивом recno(), BM filtra на сервере, ясности нет Перекомпилировать сервер под BMDBFNTX нет особого смысла Так и сборка с __BM = Yes, как говорится, "есть не просит", а без нее не попробуешь

PSP: Вот хоть стреляйте меня, но я утверждаю, что Letodb показывает свои примущества только в случае обработки запросов с помощью udf-функций (эдакий псевдо sql-сервер с произвольно-самопальным синктасисом)))). В остальных случаях всё зависит от сетевого оборудования/соединения и дисковой системы сервера. На гигабитной сетке или, если на серваке raid-10, может случиться так, что обычный файловый режим будет быстрее.

SergKis: PSP пишет свои примущества только в случае обработки запросов с помощью udf-функций Так и не спорим об этом. Перенос деиствий на сторону сервера требует опр. затрат., написания кода ... На первом этапе, мин. телодвижениями, надо встроить летодб в старый код, не проиграв сильно в скорости. Если был массив recno(), наверно надо применить BM фильтр, был SET FILTER TO ... сделать его оптимистическим, ... В "нашей" версии есть простой механизм для работы на сервере, заменяя SET FILTER TO ...[pre2] leto_ParseRecords( leto_Udf('UDF_dbEval', <xScope>, <xScopeBottom>, <xOrder>, <cFilter>, <lDeleted> ) ) while ! eof() ... skip enddo dbInfo( DBI_CLEARBUFFER ) [/pre2] Для меня, к примеру, 180 сек или 120 сек без разницы (клиенту в большинстве тоже, он так и так не успеет кофе попить), 15 минут и 5 мин. есть разница.

Sergy: PSP пишет: Вот хоть стреляйте меня, но я утверждаю, что Letodb показывает свои примущества только в случае обработки запросов с помощью udf-функций (эдакий псевдо sql-сервер с произвольно-самопальным синктасисом)))). В остальных случаях всё зависит от сетевого оборудования/соединения и дисковой системы сервера. На гигабитной сетке или, если на серваке raid-10, может случиться так, что обычный файловый режим будет быстрее. Не очень понятно, какая разница - как именно будут выбраны записи: при помощи UDF функции или при помощи server-side простейшего фильтра. Самое главное, что во время этой выборки - на клиент попадет то и только то, что требуется, ничего лишнего: ни неподходящих по условию записей, ни страниц из индексных файлов, необходимых, чтобы выбрать эти самые записи. Ничего. Только запрошенные данные. И совершенно понятно, что в определенных условиях, например, при наличии 2х1Gb сетки, быстрого диска и тп можно в теории получить аналогичный результат. Но что будет с этой сеткой, если будет хотя-бы десяток юзеров, гоняющих туда-сюда гигабайтные таблицы? А если юзеров больше? А если канал - не локальная сеть, а интернет? А конкретно LetoDB(f) тут вообще ни причем. Просто удобный движок для ленивых талантливых программистов, написавших много (ооочень много?) лет назад удачный код, который оказался актуален до сих пор. И второй момент: безопасность/целостность данных. Пофиг даже на скорость. Гораздо правильнее, когда в данные не лезет 10-50-100 разных машин с вирусами/шифровальщиками/битой_памятью/плохо_обжатыми_коннекторами/высохшими_аккумуляторами_ИБП/итп (тьфу*3). Есть сервер и на нем локальный диск. Один процесс оперирует всеми локальными данными, высылая по сети только РЕЗУЛЬТАТ. А уж будет это Oracle или MongoDB - тоже пофиг. Кому милее арбуз, а кому - свиной хрящик.

PSP: Sergy пишет: какая разница - как именно будут выбраны записи: при помощи UDF функции или при помощи server-side простейшего фильтра В таком случае, наверное, минимальная. Если фильтр возможно построить на стороне сервера. Если структура таблиц такова, что оптимизированный фильтр невозможно построить, то при использовании udf - это не проблема. Функции работатют с данными на стороне сервера полностью. Я имел в виду, что простая замена rdd вряд ли решит проблему скорости. Вы убедились на своем же примере. И второй момент: безопасность/целостность данных. Пофиг даже на скорость. Гораздо правильнее, когда в данные не лезет 10-50-100 разных машин с вирусами/шифровальщиками/битой_памятью/плохо_обжатыми_коннекторами/высохшими_аккумуляторами_ИБП/итп (тьфу*3). Есть сервер и на нем локальный диск. Один процесс оперирует всеми локальными данными, высылая по сети только РЕЗУЛЬТАТ. И чем тут udf не вписывается?)) зы. А так-то спорить тут не о чем. Каждый делает, как привык)

Pasha: Sergy пишет: Сорри, не в 40, а в 24 раза LetoDBf получается медленнее, чем DBFNTX по сети: Да, так и есть. Правда, я проверил с не с ntx, а с cdx, и не с letodbf, а с letodb, но разница получилась такого порядка. Причем локально разница меньше, в 8-9 раз. Думаю, что причину здесь уже разжевали, повторяться не буду.

Sergy: Спасибо за тест, значит дело не в форке и не кривых настройках у меня. Всё понятно, кроме этой фразы: Pasha пишет: Причем локально разница меньше, в 8-9 раз. Как это "локально разница меньше" ?

Pasha: По сети разница ~ в 24 раза (dbfntx быстрее) Локально - в 8-9 раз (dbfntx быстрее)

Sergy: Значит dbfntx по сети в три раза быстрее, чем локально ??? Как такое вообще может быть ?

Pasha: Нет. И не вообще медленнее, а конкретно эта операция. Для dbfntx: по сети медленнее (грубо) в 3 раза чем локально Для letodb: по сети медленнее в 10 раз чем локально Локально letodb медленнее чем dbfntx локально в 8 раз Получаем (буржуазная лженаука арифметика): по сети letodb медленнее чем dbfntx в 8*10/3 ~ в 24 раза

Sergy: Выяснил - всё нормально у LetoDB(f) с производительностью в режиме GO INT(RAND() * RECCOUNT()). Мой косяк: вместо RECCOUNT() в коде использовался nSize: [pre2] aRecords := ARRAY(nSize) FOR i:=1 TO nSize aRecords[ i ] := INT(RAND()*nSize) NEXT i [/pre2] Соотв. вместо того, чтобы "прыгать" по всей таблице, выполнялись GO только по первым 10000 записям, которые легко помещаются в кэш винды. После исправления всё встало на свои места:[pre2] ... Testing 10000 jumps via DBFNTX: 59614 ms, 167.7 jumps/sec Testing 10000 jumps via LETO: 6508 ms, 1536.6 jumps/sec ... Testing 10000 jumps via DBFNTX: 36694 ms, 272.5 jumps/sec Testing 10000 jumps via LETO: 6717 ms, 1488.8 jumps/sec ... Testing 10000 jumps via DBFNTX: 22936 ms, 436.0 jumps/sec Testing 10000 jumps via LETO: 6655 ms, 1502.6 jumps/sec[/pre2] Причем с каждым запуском DBFNTX "разгоняется", но все равно, до Leto ему далеко. Посыпаю голову пеплом. Leto рулит.



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