En corto
| |
| |
Cada COMMIT que cambie el resultado de esa consulta dispara on_change con los rowid afectados. Sin polling, sin triggers, sin GoldenGate.
Por qué
Cuando otro sistema necesita reaccionar a cambios en Oracle (invalidar una caché, replicar hacia otra base, disparar un proceso), lo habitual es preguntar cada N segundos con WHERE updated_at > :last. Funciona hasta que no: filas sin columna de fecha, borrados que no dejan rastro, carga inútil sobre la base.
Continuous Query Notification invierte el flujo: registras una consulta y Oracle te llama cuando cambia. En modo query (SUBSCR_QOS_QUERY) avisa solo si cambia el resultado de esa consulta; en modo object (sin ese flag) avisa ante cualquier cambio en las tablas implicadas. SUBSCR_QOS_ROWIDS incluye los rowid en la notificación, con lo que el callback puede leer exactamente esas filas.
El caso: replicar Oracle a PostgreSQL
oracle_fdw deja consultar tablas de Oracle desde PostgreSQL como si fueran locales, y es tentador pensar que un trigger sobre la foreign table se disparará cuando cambien los datos en Oracle. No: los triggers de una foreign table solo saltan con escrituras hechas desde PostgreSQL a través de ella. El autor de oracle_fdw lo dice tal cual: no hay forma automática de mantener sincronizada una tabla de PostgreSQL con una de Oracle desde el lado PostgreSQL; eso tiene que dispararlo Oracle.
Con CQN el pequeño servicio en Python es la pieza que falta:
| |
Los borrados llegan con row.operation & oracledb.OPCODE_DELETE y un rowid que ya no existe: borra en PostgreSQL por la clave que guardaste asociada a ese rowid, o marca la fila.
Requisitos
- Permiso:
GRANT CHANGE NOTIFICATION TO app_user, además deSELECTsobre las tablas. events=Trueen la conexión que se suscribe; sin esosubscribe()falla.- Red de vuelta. Por defecto es la base la que abre una conexión hacia tu aplicación para entregar las notificaciones: la máquina del cliente necesita una IP alcanzable desde el servidor Oracle y un puerto abierto (
subscribe(..., ip_address=…, port=…)fija cuáles). Detrás de NAT, en un contenedor o con firewall entre medias, las notificaciones simplemente no llegan y nadie avisa. client_initiated=Trueelimina ese problema: la conexión la abre tu app. Requiere modo Thick (oracledb.init_oracle_client()con Instant Client 19.4 o superior) y base 19.4 o superior. En modo Thin, el por defecto de python-oracledb, CQN funciona pero solo en el modo clásico.job_queue_processesmayor que cero en la base: las notificaciones se despachan por el job queue.
Trampas
ORA-29977: Unsupported column type for query registration in guaranteed mode: en modo query hay tipos de columna que Oracle no admite (LOB, entre otros), ySELECT *los arrastra. Lista las columnas que necesitas o quitaSUBSCR_QOS_QUERYy trabaja en modo object.- Las notificaciones no son durables: si tu servicio está caído durante un cambio, no lo recibe. Al arrancar, haz una reconciliación (por
updated_at, o completa) y después suscríbete.timeout=0ensubscribemantiene la suscripción hasta que la conexión muera. - Una suscripción por conexión viva: la conexión con
events=Truetiene que seguir abierta; ponle keepalives y reconecta con re-suscripción si se cae. - Mucho volumen (miles de commits por segundo) no es territorio de CQN: ahí entra un CDC por redo log (GoldenGate, o Debezium con su conector de Oracle vía LogMiner).
SELECTcon funciones no deterministas o sobre vistas complejas puede no registrarse en modo query; simplifica la consulta o cae a modo object.
Referencia: Continuous Query Notification en la documentación de python-oracledb y la discusión en oracle_fdw.