Per l'Edu: RaFA no me jod...
RaFA és una llançadora de procediments fàcil i ràpid de deslligar que funciona de meravella amb càrregues de les que tu saps, sense efectes col·laterals.
Per integrar-ho a qualsevol desenvolupament de paquets PL/SQL no és difícil...
Primer has d'afegir o crear, un paquet de constants... Les dels missatges de resultat i estat d'execució. Aquest exemple senzill ho il·lustra.
....CREATE OR REPLACE PACKAGE pkg_RaFA_Core AS
......c_RAFA Constant Varchar2(50):= 'RaFA';
......c_OK Constant Varchar2(50):= 'OK';
......c_WRN Constant Varchar2(50):= 'WRN';
......c_KO Constant Varchar2(50):= 'KO';
......c_Cua Constant Varchar2(50):= 'CUA';
......c_Error Constant Varchar2(50):= 'ERROR';
......c_Exec Constant Varchar2(50):= 'EXECUCIO';
......c_Fi Constant Varchar2(50):= 'FI';
......c_Inici Constant Varchar2(50):= 'INICI';
......c_Flux Constant Varchar2(50):= 'RaFA';
....FUNCTION FNC_SYSTIMESTAMP Return SYSTIMESTAMP(6);
....-- FUNCTION FNC_FES_TRACA Return Varchar2;
....END PACKAGE pkg_RaFA_Core;
..../
....CREATE OR REPLACE PACKAGE pkg_RaFA_Core AS
......c_RAFA Constant Varchar2(50):= 'RaFA';
......c_OK Constant Varchar2(50):= 'OK';
......c_WRN Constant Varchar2(50):= 'WRN';
......c_KO Constant Varchar2(50):= 'KO';
......c_Cua Constant Varchar2(50):= 'CUA';
......c_Error Constant Varchar2(50):= 'ERROR';
......c_Exec Constant Varchar2(50):= 'EXECUCIO';
......c_Fi Constant Varchar2(50):= 'FI';
......c_Inici Constant Varchar2(50):= 'INICI';
......c_Flux Constant Varchar2(50):= 'RaFA';
....FUNCTION FNC_SYSTIMESTAMP Return SYSTIMESTAMP(6) IS
......vSQL Varchar2(32767);
......vRetorn TimeStamp(6);
....BEGIN
......vSQL := 'select SysTimeStamp from dual';
......execute_immediate vSQL into vRetorn;
......If vRetorn Is Null Then
........Raise;
......End If;
......Return vRetorn;
....EXCEPTION
....-- Brut però efectiu : ·
......When Others Then
......Return Null;
....END FUNCTION FNC_SYSTIMESTAMP;
....--FUNCTION FNC_FES_TRACA Return Varchar2;
....END PACKAGE pkg_RaFA_Core;
..../
....CREATE PUBLIC SYNONYM PKG_RAFA_CORE;
..../
....-- GRANT EXECUTE ON PKG_RAFA_CORE TO :USER;
Quan un treballa en DATAs o OLTPS grans ha de racionalitzar la finestra de temps assignada, i "UN PASE" com diria el Ciborcillo, no sempre és "DIARIO" i d'una passada...
Poc temps ens quedarà per posar-hi les grapes i fer un altre PASE si ha petat... El "cullerot" apareix, li donem a la xurrera i ... ja està!: S'ha mort aquí, podem continuar des d'allà perque això ja ho hem processat, la resta ho podem llençar... a les escombraries!
Quan a un se li aveinen moltes càrregues de cop, o ja viu d'elles pensa, gairebé obsesivament, com automatitzar al màxim el procés de recuperació d'errors (Failover) de la solució existent. En altres paraules, com recuperar forces per la posada en marxa...
Jo ho he estat fent, i perxò m'he animat a explicar el RaFA, Real Application Fire Artillerist (és una broma de l'equip) per no haver de patir més de lo necessari.
Lo primer és traçar com un boig, sense por, jo et recomano crear-te una taula RAFA_CORE_LOG d'auditoria precisa dels llançaments, però de fet, amb les constants en tens prou. Lo de precisa, també va per la taula, no només a l'auditoria em refereixo al temps: has d'enregistrar l'execució amb tipus de dades com TIMESTAMP(6) per exemple. I recorda: AL "TANTO" SI EXECUTES EN PARAL·LEL!
No he posat cap escrit CREATE, a partir d'ara genèricament en direm fnc_FES_LOG...
Comencem, agafa qualsevol PROCEDURE ETL que et faci ràbia! Mira la capçalera, sino els té, li afegeixes als paràmetres, aquests dos:
......pExecucio OUT Varchar2,pResultat OUT Varchar2)
Ves cap l'IS de la definció del PROCEDURE i afegeix-li, aquestes dues, la del nom del procediment és per capturar l'esquema, el Paquet i el nom del procediment, s'hi pot afegir el procés...
......vProcedure Varchar2(100);
......ErrorWRN Exception;
I després en el BEGIN abans d'inicialitzar les variables,
......vProcedure:= <<current_procedure>>;
......pExecucio := pkg_RAFA_CORE.c_Inici;
......pResultat := pkg_RAFA_CORE.c_OK;
I en finalitzar, també haurem de tocar l'EXCEPTION, és clar...
.........
......vProcedure:= <<current_procedure>>;
......pExecucio := pkg_RAFA_CORE.c_Fi;
......pResultat := pkg_RAFA_CORE.c_OK;
....EXCEPTION
.........
......WHEN OTHERS THEN
........vProcedure:= <<current_procedure>>;
........pExecucio := pkg_RAFA_CORE.c_Error;
........pResultat := pkg_RAFA_CORE.c_KO;
.........
....END <<current_procedure>>;
En aquests moments, el teu procediment ja està integrat com un projectil RaFA! Si et fixes, no és gaire difícil imaginar que ens falta una llançadora, i potser -en un altre capítol- una cua...
NOTA: Si el tens en un paquet l'hauràs sobrecarregat i et permet migrar instantàniament si tot ha anat com cal esperar. Si no ho has fet, fes-ho.
Repetim l'operació en tots els procediments que integrin el procés. I tot seguit ens anem al procediment "LLENÇA-HO_TOT" que ve a ser un "dispara"! Segur que si haguessis de disparar un canó d'artilleria, t'asseguraries de que el projectil anterior, s'ha disparat bé... doncs fem-ho.
....CREATE OR REPLACE PACKAGE BODY pkg_Exemple_0805 AS
....PROCEDURE Executa_tot
....IS
......vProces Varchar2(8);
......vProcedure Varchar2(100);
......param1 Varchar2(100);
......param2 Varchar2(100);
......vRAFALOG Varchar2(2000);
....BEGIN
....-- Es pot deslligar de moltes maneres, un literal per
....-- informar el procés, a títol d'exemple només...
........vProces := '08052300';
........vProcedure := 'Executa_Tot';
........pExecucio := pkg_RAFA_CORE.c_Inici;
........pResultat := pkg_RAFA_CORE.c_OK;
........-- Incialitzem variables...
........param1 := 'Valor 1';
........param2 := 'Valor 2';
........-- Executem els procediments seqüencialment.
........PROCEDIMENT_1(param1, param2, vResultat, vExecució);
........PROCEDIMENT_2(param1, param2, vResultat, vExecució);
...........
........PROCEDIMENT_n(param1, param2, vResultat, vExecució);
........vProcedure:=
........pExecucio := pkg_RAFA_CORE.c_Fi;
........pResultat := pkg_RAFA_CORE.c_OK;
....EXCEPTION
........When Others Then
..........vProcedure:=
..........pExecucio := pkg_RAFA_CORE.c_Error;
..........pResultat := pkg_RAFA_CORE.c_KO;
...........
......END Executa_tot;
......PROCEDIMENT_1(param1 Varchar2, param2 Varchar2) is
...........
....END pkg_Exemple_0805;
Donem un primer pas en el paquet que tenia (o acabem de fer) el procediment. I li afegim l'enregistrament paral·lel per no concórrer en els mateixos segments de LOG de l'execució...
....CREATE OR REPLACE PACKAGE BODY pkg_Exemple_0805 AS
......PROCEDURE Executa_tot IS
........vProces Varchar2(4);
........vProcedure Varchar2(30);
........vResultat pkg_RAFA_CORE.c_Resultat%Type;
........vExecucio pkg_RAFA_CORE.c_Execucio%Type;
........vRAFALOG Varchar2(2000);
........param1 Varchar2(100);
........param2 Varchar2(100);
......BEGIN
........vProces := 'PKG_EXEMPLE_0805';
........vProcedure := 'Executa_Tot';
........param1 := 'Valor 1';
........param2 := 'Valor 2';
........vRAFALOG := v_Procedure' Iniciant execució...';
........-- Afegim traça en iniciar el procediment.
........PKG_RAFA_CORE.FNC_FES_LOG(
..........pkg_RAFA_CORE.FNC_SYSTIMESTAMP,pResultat,pExecucio,
..........vProces,vProcedure,param1||'#'||param2,vRAFALOG
........);
...........
........pExecucio := pkg_RAFA_CORE.c_Fi;
........pResultat := pkg_RAFA_CORE.c_OK;
........vRAFALOG := v_Procedure' Fi d''execució...';
........-- Afegim traça en acabar el procediment.
........PKG_RAFA_CORE.FNC_FES_LOG(
..........pkg_RAFA_CORE.FNC_SYSTIMESTAMP,pResultat,pExecucio,
..........vProces,vProcedure,param1||'#'||param2,vRAFALOG
........);
....EXCEPTION
......When Others Then
........vProcedure:=
........pExecucio := pkg_RAFA_CORE.c_Error;
........pResultat := pkg_RAFA_CORE.c_KO;
........vRAFALOG := PKG_RAFA_CORE.C_ERROR||'vProcedure||
........' 'substr(SQLERRM,1,200);
........PKG_RAFA_CORE.FNC_FES_LOG(
........pkg_RAFA_CORE.FNC_SYSTIMESTAMP,pResultat,pExecucio,
..........vProces,vProcedure,param1||'#'||param2,vRAFALOG
........);
...........
Ara ja tenim la llançadora llesta, en condicions de disparat amb precisió, només ens cal executar-la i comprovar que traça correctament.
..DATA_EXECUCIO..............ESTAT.....RESULTAT.PROCES.PROC.MISSATGE
..--------------------------.--------..--------.------.----.-------------------------------------------------------
..06/07/2007 20:21:54,127984.INICI.....OK.......0805...2300 PKG_EXEMPLE_08005.EXECUTA_TOT : Iniciant Execució
..06/07/2007 20:21:56,549932.FI........OK.......0805...2300 PKG_EXEMPLE_08005.EXECUTA_TOT : Fi d''Execució
Comprovem que també enregistra quan es produeixi un error i llavors anem a tractar les crides que fem als procediments. Com he comentat abans, el resultat ens proporciona tres valors possibles, correcte, avís i error. Anem doncs, ha deslligar el tractament del resultat, es tracta d'incloure aquesta estructura de control després de cada llançament o crida.
......PROCEDIMENT_1(param1, param2, vResultat, vExecució);
......If vResultat = pkg_RAFA_CORE.c_WRN Then
........-- Fem alguna cosa...
........ElsIf vResultat = pkg_RAFA_CORE.c_KO Then
........Raise;
......End If
Aquest tractament ens permet determinar tres possibles actuacions:
....- si el procediment cridat ha finalitzat correctament, cridarem el següent,
....- si ens retorna un avís, prendrem una decisió,
....- i si ha finalitzat amb error llavors interromprem la càrrega...
Per l'execució farem el mateix, el tractament pot esdevenir tan complex com els nostres requeriments demanin.
El codi següent il·lustra com deslligar un RaFA amb la instrucció GOTO, sovint oblidada en els entorns de desenvolupament actuals...
..CREATE OR REPLACE PACKAGE BODY pkg_Exemple_0805 AS
....PROCEDURE Executa_tot IS
......vProces Varchar2(4);
......vProcedure Varchar2(30);
......vResultat pkg_RAFA_CORE.c_Resultat%Type;
......vExecucio pkg_RAFA_CORE.c_Execucio%Type;
......vRAFALOG Varchar2(2000);
......param1 Varchar2(100);
......param2 Varchar2(100);
....BEGIN
......vProces := 'PKG_EXEMPLE_0805';
......vProcedure := 'Executa_Tot';
......param1 := 'Valor 1';
......param2 := 'Valor 2';
......vRAFALOG := v_Procedure' Iniciant execució..
......-- Afegim traça en iniciar el procediment.
......PKG_RAFA_CORE.FNC_FES_LOG(
......pkg_RAFA_CORE.FNC_SYSTIMESTAMP,pResultat,pExecucio,
........vProces,vProcedure,param1||'#'||param2,vRAFALOG
......);
......-- Cridem el primer procediment.
......PROCEDIMENT_1(param1, param2, vResultat, vExecució);
......If vResultat = pkg_RAFA_CORE.c_WRN Then
........GOTO PROCEDIMENT3;
......ElsIf vResultat = pkg_RAFA_CORE.c_KO Then
........Raise;
......End If
......<<PROCEDIMENT2>>
......PROCEDIMENT_2(param1, param2, vResultat, vExecució);
......If vResultat = pkg_RAFA_CORE.c_WRN Then
........GOTO PROCEDIMENT4;
......ElsIf vResultat = pkg_RAFA_CORE.c_KO Then
........Raise;
......End If
......<<PROCEDIMENT3>>
......PROCEDIMENT_3(param1, param2, vResultat, vExecució);
.........
......<
......PROCEDIMENT_n(param1, param2, vResultat, vExecució);
......If vResultat = pkg_RAFA_CORE.c_WRN Then
........GOTO EOF;
......ElsIf vResultat = pkg_RAFA_CORE.c_KO Then
........Raise;
......End If
......<<EOF>>
......pExecucio := pkg_RAFA_CORE.c_Fi;
......pResultat := pkg_RAFA_CORE.c_OK;
......vRAFALOG := v_Procedure' Fi d''execució...';
......-- Afegim traça en acabar el procediment.
......PKG_RAFA_CORE.FNC_FES_LOG(
........pkg_RAFA_CORE.FNC_SYSTIMESTAMP,pResultat,pExecucio,
........vProces,vProcedure,param1||'#'||param2,vRAFALOG
......);
....EXCEPTION
.........
......When Others Then
........vProcedure:=
........pExecucio := pkg_RAFA_CORE.c_Error;
........pResultat := pkg_RAFA_CORE.c_KO;
........vRAFALOG := PKG_RAFA_CORE.C_ERROR'vProcedure||
..........' 'substr(SQLERRM,1,200);
........PKG_RAFA_CORE.FNC_FES_LOG(
..........pkg_RAFA_CORE.FNC_SYSTIMESTAMP,pResultat,pExecucio,
..........vProces,vProcedure,param1||'#'||param2,vRAFALOG
........);
.........
Si us fixeu, a la taula de registre RaFA només tindrem l'inici i el fi d'execució del procediment que executa la càrrega, restarà llavors saber quin procediment estem cridant, per enregistrar-ho tot...
Tenim infinitat d'aproximacions per fer-ho, però una d'elles és fer que el procediment que cridem, traci en el registre RaFA...
....PROCEDURE PROCEDIMENT_n (param1 Varchar2, param2 Varchar2,
......pResultat OUT Varchar2, pExecucio pVarchar2)
....IS
......vProcedure Varchar2(100);
......ErrorWRN Exception;
....BEGIN
......vProcedure:=
......pExecucio := pkg_RAFA_CORE.c_Inici;
......pResultat := pkg_RAFA_CORE.c_OK;
......vRAFALOG := v_Procedure' Iniciant execució..
......-- Afegim traça en iniciar el procediment.
......PKG_RAFA_CORE.FNC_FES_LOG(
........pkg_RAFA_CORE.FNC_SYSTIMESTAMP,pResultat,pExecucio
........vProces,vProcedure,param1||'#'||param2,vRAFALOG
......);
.........
Amb aquesta aproximació només detectarem errors d'execució si el procediment cridat s'arriba a executar, en cas de no fer-ho la llançadora retornaria l'error mitjançant el seu tractament d'excepcions.
Per tant, per acabar de deslligar la solució RaFA hem de traçar les crides als procediments i ho podrem donar per acabat...
.........
......-- Cridem el primer procediment.
......vRAFALOG :=vProcedure' : Executant PROCEDIMENT_1';
......PROCEDIMENT_1(param1, param2, vResultat, vExecució);
......
......If vResultat = pkg_RAFA_CORE.c_WRN Then
........vRAFALOG := vProcedure' : Avís 'vRAFALOG;
........vRAFALOG := vRAFALOG' -> ''PROCEDIMENT_2 no s''executa';
........PKG_RAFA_CORE.FNC_FES_LOG(
..........pkg_RAFA_CORE.FNC_SYSTIMESTAMP,pResultat,pExecuio
..........vProces,vProcedure,param1'#'param2,vRAFALOG
......);
........GOTO PROCEDIMENT3;
......ElsIf vResultat = pkg_RAFA_CORE.c_KO Then
........Raise;
......End If;
......><<PROCEDIMENT2>>
......vRAFALOG :=vProcedure' : Executant PROCEDIMENT_2';
......PROCEDIMENT_1(param1, param2, vResultat, vExecució);
.........
El registre d'una càrrega utilitzant la tecnologia RaFA és com la que es mostra en el llistat següent:
..DATA_EXECUCIO..............ESTAT.....RESULTAT.PROCES.PROC.MISSATGE
..--------------------------.--------..--------.------.----.-------------------------------------------------------
..06/07/2007 20:21:54,127984.INICI.....OK.......0805...2300 PKG_EXEMPLE_08005.EXECUTA_TOT : Iniciant Execució
..06/07/2007 20:21:54,967894.EXECUCIO..OK.......0805...2300 PKG_EXEMPLE_08005.EXECUTA_TOT : Executant PROCEDIMENT_1
..06/07/2007 20:21:54,998048.EXECUCIO..OK.......0805...7600 PKG_EXEMPLE_08005.PROCEDIMENT_1 : Iniciant Execució
..06/07/2007 20:21:55,000179.EXECUCIO..OK.......0805...7600 PKG_EXEMPLE_08005.PROCEDIMENT_1 : Fi d''execució
..06/07/2007 20:21:55,000395.EXECUCIO..OK.......0805...2300 PKG_EXEMPLE_08005.EXECUTA_TOT : Executant PROCEDIMENT_2
..06/07/2007 20:21:55,004782.EXECUCIO..OK.......0805...7700 PKG_EXEMPLE_08005.PROCEDIMENT_2 : Iniciant Execució
..06/07/2007 20:21:55,719856.EXECUCIO..OK.......0805...7700 PKG_EXEMPLE_08005.PROCEDIMENT_2 : Fi d''execució
..06/07/2007 20:21:55,800059.EXECUCIO..OK.......0805...2300 PKG_EXEMPLE_08005.EXECUTA_TOT : Executant PROCEDIMENT_3
..06/07/2007 20:21:55,910027.EXECUCIO..OK.......0805...7900 PKG_EXEMPLE_08005.PROCEDIMENT_3 : Iniciant Execució
..06/07/2007 20:21:56,478191.EXECUCIO..OK.......0805...7900 PKG_EXEMPLE_08005.PROCEDIMENT_3 : Fi d''execució
..06/07/2007 20:21:56,549932.FI........OK.......0805...2300 PKG_EXEMPLE_08005.EXECUTA_TOT : Fi d''Execució
En properes aportacions deslligarem el tractament del resultat i l'execució de cada procediment, per tal d'incorporar mecanismes de recuperació d'errors, dins de la mateixa càrrega. També crearem un sistema d'encuat de procediments que ens permeti, reprendre l'execució de la càrrega si s'ha produït un error controlat.

5 comentarios:
En els codis d'exemple les etiquetes del GOTOS i totes les expresions que utilitzes els símbols "<" i ">" no surten...
¿Què significa sobrecarregar un procediment? No ho acabo d'entendre ben bé, perquè ho fas...
Hi ha un ball de bastons entre pResultat, vResultat i pExecucio i vExecucio si executes el codi tal com dius no es generen els LOGS que poses.
Pots posar alguna versió que funcioni...
:)
Anònim2: Tens raó, el codi que he escrit està editat...
Si desitges un exemple que funcioni, només has de d'enviar un correu a onacorpuscle@gmail.com i te'l faig arribar.
Gràcies pels vostres comentaris, de veritat!
Karlos: La teva llançadora té un ús il·lícit de la instrucció GOTO, revisa aquesta entrada t'ho explica de manera resumida.
http://onacorpuscle.blogspot.com/2007/07/pls-00375-adreant-se-vers-un-dest.html
Anonimo1: He corregit els càracters especials, com demanaves, a la propera entrada Corregint l'absència d'un DBA I: Sobrecarregant Procediments empaquetats, respondré la teva pregunta.
Salutacions.
Karlos: Tampoc pots fer un GOTO dins del When Others de la secció d'Excepcions.
De fet, pensa que mentre s'executa l'excepció el bloc BEGIN corresponent a deixat d'executar-se (ha fet un Raise explícit interrompent-lo en no poder continuar l'execució, per tant no pots adreçar l'execució una altra vegada a ell...
El que et recomano, veient la teva llançadora, és que utilitzis blocs niats, és a dir, incloure un BEGIN / EXCEPTION / END, dins del bloc principal i te'n sortiràs prou bé.
Si llances l'excepció dins del bloc niat i la tractes informant la variable de resultat quan acaba bé, amb avís o error, per exemple, pots reprendre l'execució del bloc principal.
Ho explicaré en un altra entrada, si cal.
Agraeixo el teu comentari del correu, però pensa que no sempre és així, tot depèn del temps lliure que disposo...
Publica un comentari