Files
comun/utile/Teste/cache_anaf/test_timp_masa_peste1000.prg
Marius Mutu 5c8a201438 Cache ANAF: verificarea in masa a codurilor fiscale, pe transe (SVN r17935)
Verificarea "in masa" (VerificaListaCIF ... 'MASA') pica pe ORA-01795 peste 1000
de coduri, iar esecul trimitea tot lotul la ANAF, cu pauze de o secunda la fiecare
100 de coduri. Pe langa asta, fiecare rand servit din cache era salvat inapoi in
istoric si logat separat.

- programe/validare.prg: CitesteIstoricPentruCoduri citeste pe transe de maxim
  1000 de coduri legate cu OR in aceeasi interogare (fara obiecte noi in baza,
  compatibil cu Oracle 10g); plasa de siguranta cere codurile neacoperite o
  singura data, nu cod cu cod; SalveazaIstoricDinCursor sare peste randurile cu
  sursa CACHE*; bucla de log per rand devine o singura linie cu totalul.
- clase/overificari.vc2: completarea tabelului de parteneri foloseste index pe
  codul normalizat si SEEK in loc de LOCATE cu UDF (era patratic).
- utile/Teste/cache_anaf/: harness-uri de masurare si non-regresie pentru
  timpii de mai jos.

Masurat pe MARIUSM_AUTO, cu ANAF blocat: 1000 de coduri 5,1 s -> 0,318 s;
1001 coduri 16,6 s cu ORA-01795 -> 0,553 s fara eroare; 3000 de coduri 1,565 s;
200 de coduri din cache 3,93 s -> 0,05 s, cu 0 randuri noi in istoric;
completarea tabelului pentru 2000 de parteneri 11,97 s -> 0,028 s.
Randurile intoarse si verdictele raman identice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JCzVWfqscv1xyo5uvJiWiW
2026-08-02 07:27:42 +03:00

307 lines
11 KiB
Plaintext

* test_timp_masa_peste1000.prg - continuarea masuratorii din test_timp_verificare_masa.prg:
* 1. curata randurile ramase in ISTORIC_CODURI_FISCALE peste watermark-ul rularii anterioare
* 2. costul unei scrieri goLog (STRTOFILE additive) in functie de marimea fisierului de log
* 3. costul unui apel CitesteIstoricPentruCoduri cu UN singur cod (plasa de siguranta)
* 4. rularea completa VerificaListaCIF pe un lot de peste 1000 de coduri (ANAF blocat),
* unde interogarea de cache pica pe ORA-01795 si se ajunge cod-cu-cod
*
* Rulare: vfp9.exe -A -T "...\test_timp_masa_peste1000.prg" [watermark]
LPARAMETERS tcWatermark
SET SAFETY OFF
SET TALK OFF
SET DELETED ON
SET EXACT ON
SET CENTURY ON
SET DATE DMY
SET NULLDISPLAY TO ''
CLOSE DATABASES
PUBLIC gcTestLog, gnApeluriANAF, gcMockUltimMesaj, gnMockUltimTip, gnMockRaspuns
gcTestLog = "D:\ROA\ROACONT\COMUN\utile\Teste\cache_anaf\test_timp_masa_peste1000_log.txt"
STRTOFILE("START " + TTOC(DATETIME()) + CHR(13) + CHR(10), gcTestLog)
gnApeluriANAF = 0
gcMockUltimMesaj = ''
gnMockUltimTip = 0
gnMockRaspuns = 6
ON ERROR DO ErrP1000 WITH ERROR(), MESSAGE(), PROGRAM(), LINENO()
ON SHUTDOWN QUIT
DO D:\ROA\ROACONT\COMUN\utile\Teste\test_init_env_auto.prg WITH 'CENTRAL', 'MARIUSM_AUTO', 'ROMFASTSOFT'
IF gnHandle <= 0
DO WrLog2 WITH 'EROARE: conectare Oracle esuata - GATA'
QUIT
ENDIF
DO WrLog2 WITH 'mediu OK, schema=' + gcS
SET PROCEDURE TO "D:\ROA\ROACONT\COMUN\utile\Teste\mock_amessagebox.prg" ADDITIVE
* mock_amessagebox NU acopera apelurile de amessagebox facute din oproceduri_comune.prg
* (VFP rezolva intai in fisierul de proceduri curent), iar oexecutor.oExecuta deschide un
* formular modal la orice eroare SQL - headless asta blocheaza procesul. Deci inlocuiesc
* executorul cu o subclasa care nu deschide dialogul si numara erorile.
PUBLIC gnErorriSQL, gcUltimaEroareSQL
gnErorriSQL = 0
gcUltimaEroareSQL = ''
LOCAL loExecNou
loExecNou = CREATEOBJECT('oexec_fara_dialog')
loExecNou.nHandle = gnHandle
goExecutor = loExecNou
DO WrLog2 WITH 'executor inlocuit (fara dialog modal la eroare SQL)'
LOCAL lnWm, lcSql, lnT, lnI, lnRep
lnWm = IIF(TYPE('tcWatermark') = 'C' AND !EMPTY(tcWatermark), INT(VAL(tcWatermark)), 0)
*==========================================================
* 1. curatenie dupa rularea anterioara
*==========================================================
IF lnWm > 0
lcSql = 'SELECT COUNT(*) AS N FROM ISTORIC_CODURI_FISCALE WHERE ID > ' + TRANSFORM(lnWm)
IF goExecutor.oExecuta(lcSql, 'crsRest')
DO WrLog2 WITH 'randuri peste watermark ' + TRANSFORM(lnWm) + ': ' + TRANSFORM(crsRest.N)
USE IN (SELECT('crsRest'))
ENDIF
=goExecutor.oExecuta('DELETE FROM ISTORIC_CODURI_FISCALE WHERE ID > ' + TRANSFORM(lnWm))
=goExecutor.oExecuta('COMMIT')
DO WrLog2 WITH 'curatenie facuta (ID > ' + TRANSFORM(lnWm) + ')'
ELSE
DO WrLog2 WITH 'fara watermark - nu sterg nimic'
ENDIF
PUBLIC gnIdMaxIstoric
gnIdMaxIstoric = 0
IF goExecutor.oExecuta('SELECT NVL(MAX(ID),0) AS IDMAX FROM ISTORIC_CODURI_FISCALE', 'crsWm2')
gnIdMaxIstoric = crsWm2.IDMAX
USE IN (SELECT('crsWm2'))
ENDIF
DO WrLog2 WITH 'watermark nou = ' + TRANSFORM(gnIdMaxIstoric)
*==========================================================
* 2. costul unei scrieri de log, in functie de marimea fisierului
*==========================================================
DO WrLog2 WITH ''
DO WrLog2 WITH '=== 2. cost STRTOFILE additive (asta face goLog.Log) ==='
LOCAL lcF, lcLinie
lcLinie = REPLICATE('x', 120) + CHR(13) + CHR(10)
lnRep = 300
lcF = 'D:\ROA\ROACONT\COMUN\utile\Teste\cache_anaf\_probalog_mic.txt'
STRTOFILE('', lcF)
lnT = SECONDS()
FOR lnI = 1 TO lnRep
STRTOFILE(lcLinie, lcF, .T.)
ENDFOR
lnT = SECONDS() - lnT
DO WrLog2 WITH '2a fisier ~0 MB: ' + TRANSFORM(ROUND(lnT / lnRep * 1000, 2)) + ' ms/scriere (' + TRANSFORM(ROUND(lnT, 3)) + ' s / ' + TRANSFORM(lnRep) + ')'
lcF = 'D:\ROA\ROACONT\COMUN\utile\Teste\cache_anaf\_probalog_mare.txt'
STRTOFILE(REPLICATE(lcLinie, 40000), lcF)
DO WrLog2 WITH ' fisier mare pregatit: ' + TRANSFORM(ROUND(FSIZE(lcF) / 1048576, 2)) + ' MB'
lnT = SECONDS()
FOR lnI = 1 TO lnRep
STRTOFILE(lcLinie, lcF, .T.)
ENDFOR
lnT = SECONDS() - lnT
DO WrLog2 WITH '2b fisier ' + TRANSFORM(ROUND(FSIZE(lcF) / 1048576, 2)) + ' MB: ' + TRANSFORM(ROUND(lnT / lnRep * 1000, 2)) + ' ms/scriere (' + TRANSFORM(ROUND(lnT, 3)) + ' s)'
* pentru comparatie: acelasi volum scris cu handle deschis o singura data
lcF = 'D:\ROA\ROACONT\COMUN\utile\Teste\cache_anaf\_probalog_handle.txt'
IF FILE(lcF)
ERASE (lcF)
ENDIF
LOCAL lnH
lnH = FCREATE(lcF)
lnT = SECONDS()
FOR lnI = 1 TO lnRep
=FPUTS(lnH, lcLinie)
ENDFOR
=FCLOSE(lnH)
lnT = SECONDS() - lnT
DO WrLog2 WITH '2c acelasi volum, handle deschis o data: ' + TRANSFORM(ROUND(lnT / lnRep * 1000, 3)) + ' ms/scriere'
*==========================================================
* 3. costul plasei de siguranta: o interogare per cod
*==========================================================
DO WrLog2 WITH ''
DO WrLog2 WITH '=== 3. CitesteIstoricPentruCoduri cu UN cod (plasa de siguranta) ==='
LOCAL loVer, lnZile
loVer = CREATEOBJECT('VerificareANAFTimp2')
lnZile = loVer.ZileCache()
lcSql = 'SELECT COD_FISCAL_N FROM (SELECT DISTINCT COD_FISCAL_N FROM ISTORIC_CODURI_FISCALE'
lcSql = lcSql + ' WHERE COD_FISCAL_N IS NOT NULL AND DATAORAV_ANAF IS NOT NULL'
lcSql = lcSql + ' AND SYSDATE - DATAORAV_ANAF <= ' + TRANSFORM(lnZile)
lcSql = lcSql + ' ORDER BY COD_FISCAL_N) WHERE ROWNUM <= 1200'
=goExecutor.oExecuta(lcSql, 'crsCod2')
IF USED('cLot')
USE IN cLot
ENDIF
CREATE CURSOR cLot (cod_fiscal C(30) NULL, data D NULL)
SELECT crsCod2
SCAN
INSERT INTO cLot (cod_fiscal, data) VALUES (ALLTRIM(TRANSFORM(crsCod2.COD_FISCAL_N)), DATE())
SELECT crsCod2
ENDSCAN
USE IN (SELECT('crsCod2'))
DO WrLog2 WITH 'coduri proaspete disponibile: ' + TRANSFORM(RECCOUNT('cLot'))
IF USED('crsUnul')
USE IN crsUnul
ENDIF
lnRep = MIN(100, RECCOUNT('cLot'))
lnT = SECONDS()
SELECT cLot
GO TOP
FOR lnI = 1 TO lnRep
=loVer.CitesteIstoricPentruCoduri(ALLTRIM(NVL(cLot.cod_fiscal, '')), DATE(), 'crsUnul', 'CACHE_INDISP')
SELECT cLot
SKIP
ENDFOR
lnT = SECONDS() - lnT
DO WrLog2 WITH '3a ' + TRANSFORM(lnRep) + ' apeluri cu un singur cod = ' + TRANSFORM(ROUND(lnT, 3)) + ' s'
DO WrLog2 WITH ' = ' + TRANSFORM(ROUND(lnT / lnRep * 1000, 2)) + ' ms/cod -> ' + TRANSFORM(ROUND(lnT / lnRep * 1000, 1)) + ' s la 1000 de coduri'
*==========================================================
* 4. flux complet peste limita de 1000
*==========================================================
DO WrLog2 WITH ''
DO WrLog2 WITH '=== 4. VerificaListaCIF pe lot > 1000 coduri (ANAF blocat) ==='
IF USED('cLot1001')
USE IN cLot1001
ENDIF
CREATE CURSOR cLot1001 (cod_fiscal C(30) NULL, data D NULL)
SELECT cLot
SCAN
INSERT INTO cLot1001 (cod_fiscal, data) VALUES (cLot.cod_fiscal, DATE())
SELECT cLot
ENDSCAN
FOR lnI = RECCOUNT('cLot1001') + 1 TO 1001
INSERT INTO cLot1001 (cod_fiscal, data) VALUES (ALLTRIM(STR(9000000 + lnI)), DATE())
ENDFOR
DO WrLog2 WITH 'lot = ' + TRANSFORM(RECCOUNT('cLot1001')) + ' coduri; START ' + TTOC(DATETIME())
gnApeluriANAF = 0
lnT = SECONDS()
=loVer.VerificaListaCIF('cLot1001', DATE(), 'crsRez1001', 'MASA')
lnT = SECONDS() - lnT
DO WrLog2 WITH ' erori SQL in flux: ' + TRANSFORM(gnErorriSQL) + ' [' + LEFT(gcUltimaEroareSQL, 90) + ']'
DO WrLog2 WITH '4a TOTAL = ' + TRANSFORM(ROUND(lnT, 3)) + ' s pentru ' + TRANSFORM(RECCOUNT('cLot1001')) + ' coduri'
DO WrLog2 WITH ' randuri intoarse: ' + TRANSFORM(IIF(USED('crsRez1001'), RECCOUNT('crsRez1001'), 0))
DO WrLog2 WITH ' apeluri ANAF (blocate): ' + TRANSFORM(gnApeluriANAF)
DO WrLog2 WITH ' ultim dialog interceptat: [' + LEFT(gcMockUltimMesaj, 100) + ']'
* acelasi lot, dar sub limita (1000 de coduri) - reper
IF USED('cLot1000')
USE IN cLot1000
ENDIF
CREATE CURSOR cLot1000 (cod_fiscal C(30) NULL, data D NULL)
SELECT cLot1001
GO TOP
FOR lnI = 1 TO 1000
INSERT INTO cLot1000 (cod_fiscal, data) VALUES (cLot1001.cod_fiscal, DATE())
SELECT cLot1001
SKIP
ENDFOR
DO WrLog2 WITH 'lot de reper = ' + TRANSFORM(RECCOUNT('cLot1000')) + ' coduri'
gnApeluriANAF = 0
lnT = SECONDS()
=loVer.VerificaListaCIF('cLot1000', DATE(), 'crsRez1000', 'MASA')
lnT = SECONDS() - lnT
DO WrLog2 WITH '4b TOTAL = ' + TRANSFORM(ROUND(lnT, 3)) + ' s pentru 1000 de coduri (sub limita IN)'
DO WrLog2 WITH ' randuri intoarse: ' + TRANSFORM(IIF(USED('crsRez1000'), RECCOUNT('crsRez1000'), 0))
DO WrLog2 WITH ' apeluri ANAF (blocate): ' + TRANSFORM(gnApeluriANAF)
*==========================================================
* 5. flux la 3000 de coduri (reale + sintetice) - dovada ca scaleaza peste 1000
*==========================================================
DO WrLog2 WITH ''
DO WrLog2 WITH '=== 5. VerificaListaCIF pe lot de 3000 coduri (ANAF blocat) ==='
IF USED('cLot3000')
USE IN cLot3000
ENDIF
CREATE CURSOR cLot3000 (cod_fiscal C(30) NULL, data D NULL)
SELECT cLot1001
GO TOP
SCAN
INSERT INTO cLot3000 (cod_fiscal, data) VALUES (cLot1001.cod_fiscal, DATE())
SELECT cLot1001
ENDSCAN
FOR lnI = RECCOUNT('cLot3000') + 1 TO 3000
INSERT INTO cLot3000 (cod_fiscal, data) VALUES (ALLTRIM(STR(9500000 + lnI)), DATE())
ENDFOR
DO WrLog2 WITH 'lot = ' + TRANSFORM(RECCOUNT('cLot3000')) + ' coduri; START ' + TTOC(DATETIME())
gnApeluriANAF = 0
gnErorriSQL = 0
lnT = SECONDS()
=loVer.VerificaListaCIF('cLot3000', DATE(), 'crsRez3000', 'MASA')
lnT = SECONDS() - lnT
DO WrLog2 WITH ' erori SQL in flux: ' + TRANSFORM(gnErorriSQL) + ' [' + LEFT(gcUltimaEroareSQL, 90) + ']'
DO WrLog2 WITH '5a TOTAL = ' + TRANSFORM(ROUND(lnT, 3)) + ' s pentru ' + TRANSFORM(RECCOUNT('cLot3000')) + ' coduri'
DO WrLog2 WITH ' randuri intoarse: ' + TRANSFORM(IIF(USED('crsRez3000'), RECCOUNT('crsRez3000'), 0))
DO WrLog2 WITH ' apeluri ANAF (blocate): ' + TRANSFORM(gnApeluriANAF)
*==========================================================
DO WrLog2 WITH ''
DO CurataP1000
DO WrLog2 WITH 'GATA ' + TTOC(DATETIME())
QUIT
PROCEDURE WrLog2
LPARAMETERS tcText
STRTOFILE(TRANSFORM(tcText) + CHR(13) + CHR(10), gcTestLog, .T.)
ENDPROC
PROCEDURE ErrP1000
LPARAMETERS tnError, tcMessage, tcProgram, tnLine
DO WrLog2 WITH 'EROARE ' + TRANSFORM(tnError) + ' [' + TRANSFORM(tcMessage) + '] in ' + ;
TRANSFORM(tcProgram) + ' linia ' + TRANSFORM(tnLine)
RETURN
ENDPROC
PROCEDURE CurataP1000
IF TYPE('gnIdMaxIstoric') <> 'N' OR gnIdMaxIstoric <= 0
DO WrLog2 WITH 'curatenie SARITA - watermark invalid'
RETURN
ENDIF
=goExecutor.oExecuta('DELETE FROM ISTORIC_CODURI_FISCALE WHERE ID > ' + TRANSFORM(gnIdMaxIstoric))
=goExecutor.oExecuta('COMMIT')
DO WrLog2 WITH 'curatenie: sterse randurile cu ID > ' + TRANSFORM(gnIdMaxIstoric)
ENDPROC
DEFINE CLASS oexec_fara_dialog AS oexecutor
FUNCTION oExecuta
LPARAMETERS tcSql, tcCursor, tlProgress, tcTitluProgress, tnHandle, tlShowError, tlQuitOnError, tlReconnect
LOCAL lcSql, lcCursor, lnHandle, lnRez
lcSql = IIF(EMPTY(tcSql), This.cSql, tcSql)
lcCursor = IIF(EMPTY(tcCursor), This.cCursor, tcCursor)
lnHandle = IIF(EMPTY(tnHandle), This.nHandle, tnHandle)
lnRez = This.oExecute(lcSql, lcCursor, tlProgress, tcTitluProgress, lnHandle)
IF lnRez < 1
gnErorriSQL = gnErorriSQL + 1
gcUltimaEroareSQL = TRANSFORM(This.cEroare)
ENDIF
RETURN (lnRez >= 1)
ENDFUNC
ENDDEFINE
DEFINE CLASS VerificareANAFTimp2 AS VerificareANAF
PROCEDURE OrdineCache
LPARAMETERS tcTraseu
RETURN 'CACHE,ANAF'
ENDPROC
PROCEDURE ANAF_SincronWebService_PlatitorTva
LPARAMETERS tcCursorListaCIF, tdData, tcAlias
gnApeluriANAF = gnApeluriANAF + 1
RETURN IIF(EMPTY(tcAlias), 'crsANAF', tcAlias)
ENDPROC
ENDDEFINE