bugs bugs & bugs

Desde hace unos 7 años siempre recomiendo a la gente ir al terminal release de una version en concreto en cuanto puedan aunque no les afecte ningún bug en sus versión actual.

La gente siempre hacia caso omiso por lo de “si todo funciona no lo toques”. Tengo muchas historias que contar sobre este tipo de situaciones, voy a exponer 2 hoy.

Situacion 1: Hace tiempo un cliente tiene una BBDD de la versión 9.2.0.5 y llevaba años funcionando sin dar ningún tipo de problema y lógicamente no veía ninguna razón para actualizar la versión aunque yo le habia mencionado un par de veces que hay que estar siempre en terminal release, 9.2.0.8. Un día tuvo que importar unas filas a una de las tablas core de la aplicacion y cayó en el bug 3798351 Importing a pre-existing table may DROP the table on an error, este bug es curioso porque borra la tabla donde estas importando, una herramienta de recovery como import en vez de recuperar te borra la tabla! La tabla tenia 1200 millones de filas.

Situacion 2: Una aplicacion de mensajeria que lleva funcionando años en 9.2.0.6, otro cliente que piensa que no se debe de tocar cuando todo funciona. La aplicación hace uso extenso de LOB. Un día de repente la aplicación literalmente dejó de funcionar, todas las operaciones de DML contra las tablas de LOB se serializaban, cayeron en el bug 6376915 – HW enqueue contention for ASSM LOB segments, era cuestión de tiempo, era una bomba de relojeria. Lo peor es si hubiesen estado en la versión 9.2.0.8 el fix era simplemente aplicar un parche one-off que tarda 5 minutos en aplicar pero al estar en 9.2.0.6 Oracle no sacaba parches para las versiones non-terminal-release. Resultado subiendo a 9.2.0.8 sin probar el aplicativo con la advertencia del proveedor del aplicativo que no garantizaban un funcionamiento correcto de su aplicativo en 9.2.0.8.

SQL – STATSPACK TOP 5 EVENTS

with top_events as (
select snap_id, stime, name, null waits, round(diff/100) time
from (
    select  c.snap_id,
            to_char(c.snap_time, ‘yyyymmdd hh24′) stime,
            b.name,
            b.value,
            lag(b.value) over (partition by name order by b.snap_id) last_val,
            b.value – lag(b.value) over (partition by name order by b.snap_id) diff
    from    stats$sysstat b,
            stats$snapshot c
    where   c.snap_id = b.snap_id
    and     b.name = ‘CPU used by this session’
    and     c.snap_time between to_date(:start_date, ‘yyyymmdd hh24miss’)
                            and to_date(:end_date, ‘yyyymmdd hh24miss’)
    order by 3, 1)
where last_val is not null
union all
select snap_id, stime, event, waits, wait_seconds time
from (
     select snap_id, stime, event, rank() over (partition by snap_id order by diff_wait_time_micro desc) ranking,
            round(diff_waits) waits,
            round(diff_wait_time_micro/1000000) wait_seconds
       from (
         select  c.snap_id,
                 to_char(c.snap_time, ‘yyyymmdd hh24′) stime,
                 b.event,
                 b.total_waits,
                 lag(b.total_waits) over (partition by b.event order by b.snap_id) last_total_waits,
                 b.time_waited_micro,
                 lag(b.time_waited_micro) over (partition by b.event order by b.snap_id) last_total_time_micro,
                 b.total_waits – lag(b.total_waits) over (partition by b.event order by b.snap_id) diff_waits,
                 b.time_waited_micro – lag(b.time_waited_micro) over (partition by b.event order by b.snap_id) diff_wait_time_micro,
                 d.wait_class
         from    stats$system_event b,
                 stats$snapshot c,
                 v$system_event d
         where   c.snap_id = b.snap_id
         and     b.event_id = d.event_id
         and     d.wait_class != ‘Idle’
         and     c.snap_time between to_date(:start_date, ‘yyyymmdd hh24miss’)
                                 and to_date(:end_date, ‘yyyymmdd hh24miss’)
         order by 3, 1)
     where diff_waits is not null
)
order by 1, 5 desc)
select b.*
  from (select row_number() over (partition by snap_id order by time desc) rn, a.*
          from top_events a) b
 where rn <= 5;

SQL – AWR TOP 5 EVENTS

WITH CPU_TIME
AS
(SELECT snap_id, end_interval_time, instance_number, stat_id, stat_name, value
            FROM (SELECT /*+ leading(s,sn,sy) */
                         s.snap_id,
                         s.instance_number,
                         s.dbid,
                         s.end_interval_time,
                         sy.stat_id,
                         sy.stat_name,
                         CASE
                            WHEN s.begin_interval_time = s.startup_time
                            THEN sy.value
                            ELSE sy.value – LAG (sy.value, 1)
                                            OVER (PARTITION BY
                                                  sy.stat_id, sy.instance_number,
                                                  sy.dbid, s.startup_time
                                            ORDER BY sy.snap_id)
                         END value
                    FROM dba_hist_snapshot s,
                         dba_hist_sys_time_model sy
                   WHERE s.dbid = sy.dbid
                     AND s.instance_number = sy.instance_number
                     AND s.snap_id = sy.snap_id
                     AND s.instance_number = 1
                     AND s.end_interval_time > TO_TIMESTAMP (:start_time, ‘yyyymmdd hh24mi’)
                     AND s.end_interval_time < TO_TIMESTAMP (:end_time, ‘yyyymmdd hh24mi’)
                     — this ensures hourly data are used
                     AND to_char(trunc(s.end_interval_time, ‘mi’), ‘mi’) = ’00′
                     AND sy.stat_name= ‘DB CPU’)),
WAIT_TIME
AS
(SELECT snap_id, end_interval_time, instance_number, event_name, time_waited_micro
            FROM (SELECT s.snap_id,
                         s.instance_number,
                         s.dbid,
                         s.end_interval_time,
                         sy.event_name,
                         CASE
                            WHEN s.begin_interval_time = s.startup_time
                            THEN sy.time_waited_micro
                            ELSE sy.time_waited_micro – LAG (sy.time_waited_micro, 1)
                                                        OVER (PARTITION BY
                                                              sy.event_id, sy.instance_number,
                                                              sy.dbid, s.startup_time
                                                        ORDER BY sy.snap_id)
                         END time_waited_micro
                    FROM dba_hist_snapshot s,
                         dba_hist_system_event sy
                   WHERE s.dbid = sy.dbid
                     AND s.instance_number = sy.instance_number
                     AND s.snap_id = sy.snap_id
                     AND s.instance_number = 1
                     AND s.end_interval_time > TO_TIMESTAMP (:start_time, ‘yyyymmdd hh24mi’)
                     AND s.end_interval_time < TO_TIMESTAMP (:end_time, ‘yyyymmdd hh24mi’)
                     AND sy.wait_class != ‘Idle’
                     — this ensures hourly data are used
                     AND to_char(trunc(s.end_interval_time, ‘mi’), ‘mi’) = ’00′))
SELECT end_interval_time, event, time_seconds
  FROM (SELECT end_interval_time, instance_number, event, time_seconds, dense_rank() over (partition by snap_id order by time_seconds desc) rango
          FROM (SELECT snap_id, TO_CHAR(TRUNC (end_interval_time, ‘mi’), ‘mm-dd hh24mi’) end_interval_time,
                       instance_number, stat_name event, round(value/1000000, 2) time_seconds
                  FROM cpu_time
                 WHERE value is not null
                UNION ALL
                SELECT snap_id, TO_CHAR(TRUNC (end_interval_time, ‘mi’), ‘mm-dd hh24mi’) end_interval_time,
                       instance_number, event_name event, round(time_waited_micro/1000000, 2) time_seconds
                  FROM wait_time
                 WHERE time_waited_micro is not null))
 WHERE rango <= 5;

Por qué he dejado de usar ASMLib

Desde hace ya casi un año que he dejado de instalar ASMLib para las instalaciones de ASM en Linux. Basicamente son tres razaones:

1. Oracle deja de publicar los binarios de ASMLib para Red Hat Enterprise 6
2. ASMLib depende de la version de kernel
3. ASMBLib no soporta resize de los discos

Hay muchas implantaciones de Red Hat Enterprise y no creo que cambié de tendencia a medio plazo y Red Hat no parece que quiera publicar ASMLib como hace Suse.

Actualizar la versión de kernel obliga a actualizar ASMLib, no es que sea un trauma pero para los clientes si lo es.

Por mucho que he buscado no he encontrado la forma de poder hacer un resize de un disco bajo control de ASMLib. El propio ASM da la facilidad de poder ejecutar un resize de los discos y Linux es capaz de reconocer un disco ampliado de la cabina sin embargo la capa entre Linux y ASM, ASMLib no tiene la opcion de resize. Hay una opción no documentada que es usar kfed y modificar el metadata de los discos pero no creo que sea el camino a seguir.

Por ultimo, tampoco he notado mucha diferencia en rendimiento entre un sistema que usa ASMLib y otro no…

Una base de datos Standby para Oracle Standard Edition

En los ultimos meses me han preguntado varias veces si es posible tener una base de datos standby para Standard Edition porque el Data Guard solo es posible en Enterprise Edition.

Claro que es posible, hay dos maneras de hacerlo.

Metodo 1:
Con un backup fisico (sea en caliente o frio) incluyendo los controlfiles lo llevas al host de DR y automatizamos el envio de archived logs y con un script en cron que ejecute el comando de recover es suficiente, si simplemente arrancar esta base de datos en modo mount e ir haciendo recovers

Metodo 2:
Lo mismo que anterior pero esta vez los controlfiles lo llevamos ejecutando:

alter database create standby controlfile as ‘/tmp/lnx102.ctl’

Y la base de datos lo arrancamos en nomount y luego ejecutamos:

alter database mount standby database

Luego ya es automatizar el envio de los archived logs y ejecutar en un script de sqlplus:

RECOVER STANDBY DATABASE
AUTO

Recuerdo que un amigo me preguntó en su día que diferencia hay entre estas dos maneras de montar un Standby, encuentro tres diferencias:

1. Con el metodo 2 podemos identificar quien es el standby ejecutando
select DATABASE_ROLE from v$database
devuelve PHYSICAL STANDBY pero si fuese metodo 1 devuelve PRIMARY
2. Con el metodo 2 podemos ver el scn actual de standby ejecutando
select current_scn from v$database
Con el metodo 1 siempre devuelve 0
3. La diferencia mas importante. Se puede hacer un switchover controlado (cambio de roles) si optamos por metodo 2

A gusto de cada uno pero ambos metodos funciona. El metodo 2 es posible a partir de Oracle 7 cuando se introdujo el concepto de Standby.

Auto arranque de OSW en RHEL5

Estos dias me ha tocado hacer un poco de troubleshotting en un RAC que sufre reinicios continuos.

Para ayudar en esta tarea he instalado OSWatcher de Oracle, como el reinicio es continuo tenia que habilitar el auto arranque de OSW en los runlevels de Linux, es un Red Hat Enterprise 5 y el rpm que proporciona Oracle de crear scripts de runlevels no funciona aparentemente aunque los mismos funcionan perfectamente en RHEL4.

Parece ser que es por requiretty de sudo (no es por OSW), el script de OSW ejecuta sudo -u $USER ……………….., al estar en la consola el auto arranque da este error:

sudo: sorry, you must have a tty to run sudo

es porque en RHEL 5 requieretty esta activado por defecto. Para quitarlo

visudo
comentar la linea “Defaults requiretty”

Despues de esto ya funciona el auto arranque de OSW

Bye Bye Stonith en Clusterware 11.2.0.2… no del todo

Como es sabido muchos software de Cluster implementan el algoritmo Stonith para situaciones de posibles Split Brain y fencing, Oracle Clusterware no es una excepcion tampoco el nuevo “Grid Infrastructure” de 11gR2 hasta la version 11.2.0.2.

Me consta desde hace ya casi 2 años hablando con un conocido de un foro un fabricante de equipamientos de Telco no estaba muy contento con la resolucion de Split Brain con Stonith en Oracle RAC y habian escalado su disconformidad a desarrollo de Oracle, argumentaban que en un nodo de RAC muchas veces hay mas aplicaciones corriendo y un fast reboot obliga a realizar un failover de todos los procesos del nodo, tienen su razon pero el algoritmo de Stonith no es nuevo y lleva funcionando muchisimo tiempo.

Oracle les hizo caso y en la 11.2.0.2 en vez del fencing por reboot introduce una funcionalidad denominado “reboot-less node fencing”, en vez de un fast reboot el Clusterware intenta en lo posible ejecutar shutdown de todos los recursos de Cluster del nodo problematico, los procesos de E/S son los primeros a ser terminados para evitar corrupcion de datos. En situaciones donde los recursos no terminan correctamente es cuando el Clusterware ejecutara un fast reboot o mediante el mecanismo “remote node-termination” con IPMI para reiniciar el nodo.

Una prueba para observar este comportamiento nuevo es tirar del cable del interconnect donde antes en un RAC de dos nodos siempre se reiniciaba el nodo 2 ahora ya no, simplemente se queda parado.