Le (dis)avventure con Documentum
Su Documentum, un prodotto di Gestione Contenuti (adattato per contenuti multimediali), esiste un noto bug, risolto
con le versioni successive. Attualmente stiamo utilizziando la versione 5.2 SP2 su Oracle 8.1.7.4 Il bug consiste nel fatto che a
seguito di una procedura di aggiornamento (refresh) full del catalogo di Documentum, tale procedura fallisce con
l’eccezione: ORA-1652.
Riporto l’errore nella sua interezza, ripreso direttamente dal manuale di Oracle 8.1.7:
ORA-01652 unable to extend temp segment by string in tablespace string
Cause: Failed to allocate an extent for temporary segment in tablespace.
Action: Use ALTER TABLESPACE ADD DATAFILE statement to add one or more files
to the tablespace indicated
A seguito di tale problema allora, inviamo al nostro DBA una e-mail:
> Ciao,
> ho un job Documentum che fallisce restituendo questo errore:
>
> [DM_DATA_DICT_E_CURSOR_ERROR]error: "A database error has occurred during the creation
> of a cursor ('ORA-01652: unable to extend temp segment by 64 in tablespace TEMP)"
>
> Penso che ci siano due possibilità per aggirare il problema:
> i) aumentare la sort area size
> ii) creare un nuovo Tablespase TEMP2 e renderlo di default per l'utente cmwind
>
> Purtroppo ho la necessità di far girare questo maledetto job... E' propedeutico per effettuare dei rilasci
> programmati da tempo...Possiamo intanto aumentare semplicemente la dimensione del tablespace TEMP?
> In questo caso non saprei dirti nemmeno la dimensione massima... Dovremo andare a tentativi...
>
> Mi fai sapere per favore quanto prima?
Non sapendo esattamente quale fosse il problema e non potendo accedere a tutte le risorse messe a disposizione da Oracle,
le due soluzioni proposte, sembrano alquanto plausibili e veloci. Non che siano le uniche,
ma diciamo che sono quelle che ci sono venute in mente. In realtà, avremmo potuto suggerire anche un’altra
soluzione:
Il tablespace TEMP2, dove essere LOCALLY MANAGED UNIFORM SIZE ed impostare la SORT_AREA_SIZE alla dimensione
del SEGMENTO specificato nella creazione del tablespace TEMP. A questo punto, si può provare a lanciare nuovamente
la procedura. Se il problema si ripresentava, possiamo provare a raddoppiare la SORT_AREA_SIZE e rilanciare la procedura.
Almeno è un tentativo costruttivo di procedere. Con questo voglio dire che occorre essere propositivi e
fornire idee costruttive sensate.
Vediamo invece cosa ci risponde il DBA.
> Ciao,
> ho notato che il tbs in questione ha gia 6G a disposizione.
> E questi 6G sono tutti free.
> Tale spazio mi sembra più che sufficiente per un attività anche molto pesante.
> Direi di procedere in questo modo: ci mettiamo d'accordo per lanciare in orario d'ufficio il job, e noi monitoriamo
> l'utilizzo del TEMP in real time. Se vediamo che effettivamente va aumentato,
> o è necessaria una attività diversa, lo facciamo al volo.
> I prossimi giorni io non ci sarò per cui per tale attività ti puoi fasare con “nome di un collega”.
Dunque:
1) C’è da dire che la procedura fallita era stata eseguita già con il tablespace grosso 6GB. Lanciarla
nuovamente senza
apportare alcuna modifica avrebbe come effetto solamente quello di un nuovo fallimento.
2) Indipendentemente dal problema, il DBA, non ha cercato di capire quale fosse la causa, e senza indagare ha emesso una senteza:
non era possibile un tale errore perché il tablespace era grande. Ecco invece cosa facciamo, seguendo i consigli
che noi stessi abbiamo suggerito.
Nota per chi legge. La soluzione, può essere presa come workaround, per chi utilizza la stessa
versione di Documentum.
(1° tentativo)
Sul database di sviluppo 8.1.7.4, creiamo un tablespace temporary e lo ho assegnato di default all’utente in questione:
CREATE TEMPORARY TABLESPACE TEMP2 TEMPFILE 'newtemp_file_01.dbf'
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M;
Aassociamo lo schema proprietario della procedura al nuovo tablespace temp:
ALTER USER CMS TEMPORARY TABLESPACE TEM2;
modificchiamo la SORT_AREA_SIZE, nell’int.ora ad 1MB e riavviamo l’istanza. Una volta lanciata la procedura, otteniamo
l’errore ORA-1652. Potevamo immaginare che come tentativo sarebbe stato un fallimento, ma dovevamo è comunque
provarci.
Supponiamo di aver messo sotto trace la sessione (utilizzando ORADEBUG). Purtroppo, il file di trace non ci dice nulla:
evidentemente Documentum esegue la procedura di refresh in una sessione che non è quella con cui si collega
l’operatore.
(2° tentativo)
Il gioco comincia a farsi interessante. Diventa ormai una sfida. Il problema principale è che non è possibile accedere
al codice, per cui non siamo in grado di capire cosa esattamente questa procedura di aggiornamento faccia. L’unica
cosa nota, è che ad un certo punto viene sollevata l’eccesione ORA-1652. Attivaiamo allora l’evento 1652 per
tracciare l’eccezione (per maggiori dettagli, leggere la nota 218105.1 di metalink). Ecco cosa
viene scritto nell’alert.log:
Thu Feb 9 11:04:11 2006
OS Pid: 1100 executed alter system set events '1652 trace name errorstack level 3'
Thu Feb 9 11:13:07 2006
ORA-1652: unable to extend temp segment by 128 in tablespace TEMP2
Thu Feb 9 11:13:07 2006
Errors in file /app/sbrdbms/app/oracle/admin/CMSTEST/udump/ora_1235.trc:
ORA-01652: unable to extend temp segment by 128 in tablespace TEMP2
Thu Feb 9 11:40:33 2006
OS Pid: 1290 executed alter system set events '1652 trace name errorstack off'
Estratto del file
/app/sbrdbms/app/oracle/admin/CMSTEST/udump/ora_1235.trc
Oracle8i Enterprise Edition Release 8.1.7.0.1 - Production
With the Partitioning option
JServer Release 8.1.7.0.1 - Production
ORACLE_HOME = /app/sbrdbms/app/oracle/product/8.1.7
System name: Linux
Node name: cmsdbtest
Release: 2.2.19-6.2.16
Version: #1 Wed Mar 13 14:04:29 EST 2002
Machine: i686
Instance name: CMSTEST
Redo thread mounted by this instance: 1
Oracle process number: 30
Unix process pid: 1252, image: oracle@cmsdbtest (TNS V1-V3)*** SESSION ID:(25.304) 2006-02-09 11:16:41.633
*** 2006-02-09 11:16:41.633
ksedmp: internal or fatal error
ORA-01652: unable to extend temp segment by 128 in tablespace TEMP2
Current SQL statement for this session:
select * from dm_resync_dd_attr_info order by type_id asc, policy_id asc
----- Call Stack Trace -----
calling call entry argument values in hex
location type point (? means dubious value)
-------------------- -------- -------------------- ----------------------------
ksedmp()+142 CALL ksedst()+0
ksddoa()+125 CALLr 00000000
ksdpcg()+175 CALL ksddoa()+0
ksdpec()+171 CALL ksdpcg()+0
……………… cut here ………………
E così adesso conosciamo lo statement che genera problemi. Se proviamo ad eseguirlo otteniamo sempre il
solito errore ORA-1652. Osserviamo come è fatta questa tabella:
SQL> desc dm_resync_dd_attr_info
Name Null? Type
------------ -------- -------------
TYPE_NAME VARCHAR2(32)
ATTR_NAME VARCHAR2(40)
POLICY_ID VARCHAR2(16)
STATE_NAME VARCHAR2(32)
NLS_KEY VARCHAR2(5)
TYPE_ID VARCHAR2(16)
e nessun indice associato. Domanda da 1 milione di eruo: ma siamo sicuri che stiamo davvero guardando una tabella?.
Nel mio immaginario (ma credo in quello di molti), quella doveva essere proprio una tabella. Ed invece:
SQL> SELECT * FROM CAT WHERE TABLE_NAME = UPPER('dm_resync_dd_attr_info');
TABLE_NAME TABLE_TYPE
------------------------- -----------
DM_RESYNC_DD_ATTR_INFO VIEW
Benissimo. Vediamo allora come è fatta questa vista:
SQL> SELECT TEXT FROM USER_VIEW WHERE VIEW_NAME = UPPER('dm_resync_dd_attr_info');
TEXT
-----------------------------------------------------------------------
((select a.name, b.attr_name, '0000000000000000', ' ',d.dd_locales, a.r_object_id
from dm_type_s a, dm_type_r b, dm_sysobject_s c, dm_docbase_config_r d
where
a.r_object_id = b.r_object_id and c.object_name = 'cmwind'
and c.r_object_id = d.r_object_id and d.dd_locales is not
null and not exists (select /*+UNNEST */ 1 from dmi_dd_attr_info_sv e where
e.type_name = a.name and e.attr_name = b.attr_name and
e.business_policy_id = '0000000000000000' and e.nls_key =d.dd_locales))
UNION ALL
(select a.type_name, a.attr_name, a.policy_id, a.state_name,c.dd_locales, a.type_id
from dm_dd_policies_for_attrs a, dm_sysobject_s b, dm_docbase_config_r c
where b.object_name = 'cmwind' and b.r_object_id = c.r_object_id
and c.dd_locales is not null and not exists (select /*+UNNEST */ 1
from dmi_dd_attr_info_sv d where d.type_name = a.type_name and
d.business_policy_id =a.policy_id and d.state_name=a.state_name and
d.attr_name = a.attr_name and d.nls_key =c.dd_locales))
UNION ALL
(select a.type_name, a.attr_name, a.business_policy_id, a.state_name, a.nls_key,
b.r_object_id from dmi_dd_attr_info_sv a, dm_type_s b where a.type_name = b.name and
a.resync_needed = 1)
UNION ALL
(select a.type_name, a.attr_name,
a.business_policy_id, a.state_name, a.nls_key,
'0000000000000000' from
dmi_dd_attr_info_sv a where a.resync_needed = 1 and
not exists (select 1 from dm_type_s b, dm_type_r c where
b.r_object_id = c.r_object_id and b.name = a.type_name and
c.attr_name = a.attr_name))
UNION ALL
(select a.name, b.attr_name,
'0000000000000000', ' ', d.dd_locales, a.r_object_id
from dm_type_s a, dm_dd_special_attrs b, dm_sysobject_s c,
dm_docbase_config_r d where
c.r_object_id = d.r_object_id and c.object_name = 'cmwind'
and d.dd_locales is not null and not
exists (select /*+ UNNEST */ 1 from dmi_dd_attr_info_sv e
where e.type_name = a.name and
e.attr_name = b.attr_name and e.business_policy_id =
'0000000000000000' and e.nls_key =d.dd_locales))
UNION ALL
(select a.type_name, b.attr_name, a.policy_id, a.state_name,
d.dd_locales, a.type_id from
dm_dd_policies_for_type a, dm_dd_special_attrs b,
dm_sysobject_s c, dm_docbase_config_r d
where c.r_object_id = d.r_object_id and c.object_name =
'cmwind' and d.dd_locales is not null and
not exists (select /*+ UNNEST */ 1 from dmi_dd_attr_info_sv
e where e.type_name = a.type_name and
e.attr_name = b.attr_name and e.business_policy_id =
a.policy_id and e.state_name =a.state_name and e.nls_key = d.dd_locales)))
Niente male, vero? L’idea a questo punto, è quella di provare
tutti gli statement singolarmente. Uno di questi dovrebbe mettere in evidenza il problema. Se è così, forse
possiamo intervenire sullo statemnet incriminato. Ed infatti eseguendo:
select a.type_name, a.attr_name, a.policy_id, a.state_name, c.dd_locales, a.type_id
from dm_dd_policies_for_attrs a, dm_sysobject_s b, dm_docbase_config_r c
where b.object_name = 'cmwind' and b.r_object_id = c.r_object_id
and c.dd_locales is not null and not exists (select /*+ UNNEST */
1 from dmi_dd_attr_info_sv d where d.type_name = a.type_name
and d.business_policy_id = a.policy_id and d.state_name =
a.state_name and d.attr_name = a.attr_name and d.nls_key =c.dd_locales)
otteniamo l'agognato errore ORA-1652. Come risolvere la cosa? Come si vede, nel codice dellla vista è stato cablato
l'hint /*+ UNNEST */. E’ sufficiente eliminare il +
(di fatto rendendo l'hint un semplice commento)**, ed il problema si risolve. Una volta
ricreata la vista senza l’hint, la procedura di aggiornamento di Documentum viene
eseguita con successo. Un bel lavoro.
Alla prossima.
-----
** Sto ancora studiando come si comporta l'ottimizzatore quando incontra l'hint UNNEST. Per ora, ciò che posso dire
è che eliminando tale hint, il job di refresh di Documentum non da piú problemi, terminando con successo.