Страницы

Показаны сообщения с ярлыком OGG. Показать все сообщения
Показаны сообщения с ярлыком OGG. Показать все сообщения

вторник, 9 марта 2021 г.

Oracle to PostgreSQL Replication: Numbers Truncated to 6 Decimal Places

I configured an initial load from Oracle to PostgreSQL, and found that some numbers were truncated after the load, e.g. Oracle number 123.0123456789 happened to be 123.012346 in PostgreSQL.

Setup

Source: Oracle Goldengate 19.1.0.0.4

GGSCI (ogg-hub as c##ggadmin@orcl/CDB$ROOT) 9> versions

Operating System:
Linux
Version #2 SMP Fri Mar 29 17:05:02 PDT 2019, Release 4.1.12-124.26.7.el7uek.x86_64
Node: ogg-hub
Machine: Linux

Database:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production

Target: Oracle GoldenGate 19.1.0.0.200714 for PostgreSQL

GGSCI (ogg-hub as ggadmin@testdb) 8> versions

Operating System:
Linux
Version #2 SMP Fri Mar 29 17:05:02 PDT 2019, Release 4.1.12-124.26.7.el7uek.x86_64
Node: ogg-hub
Machine: Linux

Database:
PostgreSQL
Version 12.06.0000 PostgreSQL 12.6
ODBC Version 03.52.0000
Driver Information:
GGpsql25.so
Version 07.16.0353 (B0519, U0369)
ODBC Version 03.52

odbc.ini

[ODBC Data Sources]
testdb=PostgreSQL on testdb

[ODBC]
IANAAppCodePage=106
InstallDir=/u01/app/oracle/product/19/ogg_pg_home1

[testdb]
Driver=/u01/app/oracle/product/19/ogg_pg_home1/lib/GGpsql25.so
Description=PostgreSQL 12.6
Database=testdb
HostName=postgres
PortNumber=5432
TransactionErrorBehavior=2

Initial Data

There is a table in the Oracle database:
SQL> create table tc.test(
  2    id int primary key,
  3    n1 number,
  4    n2 number);

Table created.

SQL>
SQL> insert into tc.test values (0, 123.0123456789, 123.0123456789);

1 row created.

SQL> commit;

Commit complete.

SQL>
SQL> select * from tc.test;

             ID              N1              N2
--------------- --------------- ---------------
              0  123.0123456789  123.0123456789
The corresponding PostgreSQL table is this:
testdb=# create table tc.test(
testdb(#   id bigint primary key,
testdb(#   n1 numeric,
testdb(#   n2 numeric(38,10));
CREATE TABLE
testdb=# \d tc.test
                     Table "tc.test"
 Column |      Type      | Collation | Nullable | Default
--------+----------------+-----------+----------+---------
 id     | bigint         |           | not null |
 n1     | numeric        |           |          |
 n2     | numeric(38,10) |           |          |
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)

GoldenGate Initial Load Processes

Extract

GGSCI (ogg-hub) 1> view params einit

EXTRACT einit
USERIDALIAS oggadmin
RMTHOST ogg-hub, MGRPORT 7810
RMTTASK REPLICAT, GROUP rinit

TABLE pdb.tc.test;

Replicat

GGSCI (ogg-hub) 1> view params rinit

REPLICAT rinit
SETENV (PGCLIENTENCODING="UTF8")
SETENV (ODBCINI="/home/ogg/pg/odbc.ini" )
TARGETDB testdb, USERIDALIAS ggadmin
DISCARDFILE ./dirrpt/rinit.dsc, PURGE

MAP pdb.*.*, TARGET *.*;

Performing Initial Load

Extract

GGSCI (ogg-hub) 1> start extract einit

Sending START request to MANAGER ...
EXTRACT EINIT starting

Replicat Report File

***********************************************************************
               Oracle GoldenGate Delivery for PostgreSQL
 Version 19.1.0.0.200714 OGGCORE_19.1.0.0.0OGGBP_PLATFORMS_200628.2141
   Linux, x64, 64bit (optimized), PostgreSQL on Jun 29 2020 04:06:46

Copyright (C) 1995, 2019, Oracle and/or its affiliates. All rights reserved.

                    Starting at 2021-03-08 18:05:04
***********************************************************************

Operating System Version:
Linux
Version #2 SMP Fri Mar 29 17:05:02 PDT 2019, Release 4.1.12-124.26.7.el7uek.x86_64
Node: ogg-hub
Machine: x86_64
                         soft limit   hard limit
Address Space Size   :    unlimited    unlimited
Heap Size            :    unlimited    unlimited
File Size            :    unlimited    unlimited
CPU Time             :    unlimited    unlimited

Process id: 15745

Description:

***********************************************************************
**            Running with the following parameters                  **
***********************************************************************

2021-03-08 18:05:09  INFO    OGG-03059  Operating system character set identified as UTF-8.

2021-03-08 18:05:09  INFO    OGG-02695  ANSI SQL parameter syntax is used for parameter parsing.

2021-03-08 18:05:09  INFO    OGG-02095  Successfully set environment variable PGCLIENTENCODING=UTF8.

2021-03-08 18:05:09  INFO    OGG-02095  Successfully set environment variable ODBCINI=/home/ogg/pg/odbc.ini.

2021-03-08 18:05:09  INFO    OGG-01360  REPLICAT is running in Remote Task mode.
REPLICAT rinit
SETENV (PGCLIENTENCODING="UTF8")
SETENV (ODBCINI="/home/ogg/pg/odbc.ini" )
TARGETDB testdb, USERIDALIAS ggadmin

2021-03-08 18:05:09  INFO    OGG-03036  Database character set identified as UTF-8. Locale: en_US.UTF-8.

2021-03-08 18:05:09  INFO    OGG-03037  Session character set identified as UTF-8.
DISCARDFILE ./dirrpt/rinit.dsc, purge
MAP pdb.*.*, TARGET *.*;

2021-03-08 18:05:09  INFO    OGG-01815  Virtual Memory Facilities for: COM
    anon alloc: mmap(MAP_ANON)  anon free: munmap
    file alloc: mmap(MAP_SHARED)  file free: munmap
    target directories:
    /u01/app/oracle/product/19/ogg_pg_home1/dirtmp.

2021-03-08 18:05:09  INFO    OGG-25340
Database Version:
PostgreSQL
Version 12.06.0000 PostgreSQL 12.6
ODBC Version 03.52.0000

Driver Information:
GGpsql25.so
Version 07.16.0353 (B0519, U0369)
ODBC Version 03.52.

2021-03-08 18:05:09  INFO    OGG-25341
Database Language and Character Set:
SERVER_ENCODING = "UTF8"
LC_CTYPE        = "en_US.UTF-8"
TIMEZONE        = "Europe/Dublin".

***********************************************************************
**                     Run Time Messages                             **
***********************************************************************


2021-03-08 18:05:10  INFO    OGG-06506  Wildcard MAP resolved (entry pdb.*.*): MAP "PDB"."TC"."TEST", TARGET "TC"."TEST".

2021-03-08 18:05:10  INFO    OGG-02756  The definition for table PDB.TC.TEST is obtained from the trail file.

2021-03-08 18:05:10  INFO    OGG-06511  Using following columns in default map by name: id, n1, n2.

2021-03-08 18:05:10  INFO    OGG-06510  Using the following key columns for target table tc.test: id.

PostgreSQL

testdb=# select * from tc.test;
 id |     n1     |       n2
----+------------+----------------
  0 | 123.012346 | 123.0123456789
(1 row)

testdb=# \d tc.test
                     Table "tc.test"
 Column |      Type      | Collation | Nullable | Default
--------+----------------+-----------+----------+---------
 id     | bigint         |           | not null |
 n1     | numeric        |           |          |
 n2     | numeric(38,10) |           |          |
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)
It can be seen that the column n1 has value 123.012346 rather than the expected value 123.0123456789.
It is also worth noting that the column n2 defined as numeric(38,10) has the proper value as it is in the source Oracle database.

Thoughts and Fix

The issue itself is that the number was truncated to 6 decimal places. It is quite easy to find the same issue was already reported in 2018, but it is still not resolved: Golden Gate truncating decimal places for number data type post migration.

Firstly, when I encountered that issue, I tried to identify what component is causing this: GoldenGate for Oracle, GoldenGate for PostgreSQL, or may be PostgreSQL itself. GoldenGate for PostgreSQL uses Progress DataDirect driver that has an interesting setting: Unbounded Numeric Scale that has a default value of 6. Well it looked like something. I added that parameter to odbc.ini:
[testdb]
Driver=/u01/app/oracle/product/19/ogg_pg_home1/lib/GGpsql25.so
Description=PostgreSQL 12.6
Database=testdb
HostName=ora
PortNumber=5432
TransactionErrorBehavior=2
UnboundedNumericScale=10
Having rerun the load from scratch, I finally got the correct number in PostgreSQL:
testdb=# select * from tc.test;
 id |       n1       |       n2
----+----------------+----------------
  0 | 123.0123456789 | 123.0123456789
(1 row)

Conclusion

The ODBC driver used by GoldenGate truncates unbounded numeric columns to 6 decimal places by default. Thankfully, there is an option to alter it: Unbounded Numeric Scale. I am disposed to think that it is usually more reliable to specify a certain scale in the target database than relying on such settings.

вторник, 29 ноября 2016 г.

A lot of log.* files in dirbdb directory

Since I had added ENABLEMONITORING in the GLOBALS file, I noticed that $OGG_HOME/dirbdb directory started growing.
That was OGG version 12.2.0.1.160517.
The size of this directory was 23G in the test environment and the ENABLEMONITORING option was set 3 weeks ago:
-bash-4.1$ du -sh dirbdb
 23.8G   dirbdb
From that 23.8G , 23.5G was occupied by the log.* files like below:
-rw-r-----   1 oracle dba      10485760 Nov 16 18:16 log.0000015971
-rw-r-----   1 oracle dba      10485760 Nov 16 18:28 log.0000015972
-rw-r-----   1 oracle dba      10485760 Nov 16 18:41 log.0000015973
-rw-r-----   1 oracle dba      10485760 Nov 16 18:55 log.0000015974
-rw-r-----   1 oracle dba      10485760 Nov 16 19:08 log.0000015975
-rw-r-----   1 oracle dba      10485760 Nov 16 19:20 log.0000015976
-rw-r-----   1 oracle dba      10485760 Nov 16 19:32 log.0000015977
-rw-r-----   1 oracle dba      10485760 Nov 16 19:43 log.0000015978
-rw-r-----   1 oracle dba      10485760 Nov 16 19:55 log.0000015979
-rw-r-----   1 oracle dba      10485760 Nov 16 20:07 log.0000015980
-rw-r-----   1 oracle dba      10485760 Nov 16 20:19 log.0000015981
-rw-r-----   1 oracle dba      10485760 Nov 16 20:31 log.0000015982
These files were generated approximately every ten minutes and each of them was 10M in size that made up 1.4G daily.
Having searched through MOS and Internet search engines, I stumbled across this MOSC Thread: Monitoring - Berkeley Database.
So I created a new DB_CONFIG file within the dirbdb directory and put this line into it:
-bash-4.1$ cat dirbdb/DB_CONFIG 
set_flags DB_LOG_AUTOREMOVE 
Then I restarted my OGG processes and discovered that these log files were gone.
Now there are only two log files that are kept:
[oracle@host ggs]$ ls -ltr dirbdb/log*
-rw-r-----   1 oracle   dba      10485760 Nov 28 13:31 dirbdb/log.0000017532
-rw-r-----   1 oracle   dba      10485760 Nov 28 13:41 dirbdb/log.0000017533
[oracle@host ggs]$ ls -ltr dirbdb/log*
-rw-r-----   1 oracle   dba      10485760 Nov 28 13:42 dirbdb/log.0000017533
-rw-r-----   1 oracle   dba      10485760 Nov 28 13:51 dirbdb/log.0000017534
[oracle@host ggs]$ ls -ltr dirbdb/log*
-rw-r-----   1 oracle   dba      10485760 Nov 29 07:21 dirbdb/log.0000017625
-rw-r-----   1 oracle   dba      10485760 Nov 29 07:22 dirbdb/log.0000017626
Looks like another option that should be added in the environments in which ENABLEMONITORING is set.
Yet, MOS returns no hits apart from the aforementioned MOSC thread.

понедельник, 31 октября 2016 г.

Excluding DDL in IE (Integrated Extract) using DBMS_STREAMS_ADM.SET_TAG

A couple of months ago I had to configure OGG IE (Oracle GoldenGate Integrated Extract) to exclude specific DDL from replication.
Having worked a lot with Oracle Streams, I decided to utilize DBMS_STREAMS_ADM.SET_TAG for that task.
I found a relevant MOS document How to exclude ddl in IE (integrated extract) issued from a specific user? (Doc ID 2107293.1) pretty fast.
The document suggested putting TRANLOGOPTIONS EXCLUDETAG into an extract parameter file and than all DDL statements preceeding with DBMS_STREAMS_SET.TAG should not be replicated.
Unfortunately the provided solution did not work for me.
I used OGG Server 12.2.0.1.160823 on Linux x86-64 (which means that I applied the latest bundle patch available at the moment).
The issue can be easily reproduced.
The database version used in this test was 12.1.0.2.160719.
Here are my extract and replicat parameter files:
GGSCI (misha2 as tc_ogg_replicat@db2) 112> view params etag

EXTRACT ETAG

USERIDALIAS db1_tc_ogg_extract
TRANLOGOPTIONS EXCLUDETAG 34
LOGALLSUPCOLS
EXTTRAIL ./dirdat/tc
DDL INCLUDE ALL

TABLE TC.*;

GGSCI (misha2 as tc_ogg_replicat@db2) 113> view params rtag

REPLICAT RTAG

USERIDALIAS db2_tc_ogg_replicat

MAP TC.*, TARGET TC.*;
Test schema TC will be used for demonstration and I setup the extract to exclude any LCRs with tag 34.
Then I am going to create and start the OGG processes with the following script:
GGSCI (misha2 as tc_ogg_replicat@db2) 138> alter credentialstore add user tc_ogg_extract@db1 password tc_ogg_extract alias db1_tc_ogg_extract


Credential store in ./dircrd/ altered.

GGSCI (misha2 as tc_ogg_replicat@db2) 139> alter credentialstore add user tc_ogg_replicat@db2 password tc_ogg_replicat alias db2_tc_ogg_replicat


Credential store in ./dircrd/ altered.

GGSCI (misha2 as tc_ogg_replicat@db2) 140> 

GGSCI (misha2 as tc_ogg_replicat@db2) 140> dblogin useridalias db1_tc_ogg_extract

Successfully logged into database.

GGSCI (misha2 as tc_ogg_extract@db1) 141> add extract etag integrated tranlog begin now

EXTRACT (Integrated) added.


GGSCI (misha2 as tc_ogg_extract@db1) 142> add exttrail ./dirdat/tc extract etag

EXTTRAIL added.

GGSCI (misha2 as tc_ogg_extract@db1) 143> register extract etag database


2016-10-31 12:46:49  INFO    OGG-02003  Extract ETAG successfully registered with database at SCN 39029510.

GGSCI (misha2 as tc_ogg_extract@db1) 144> 

GGSCI (misha2 as tc_ogg_extract@db1) 146> dblogin useridalias db2_tc_ogg_replicat

Successfully logged into database.

GGSCI (misha2 as tc_ogg_replicat@db2) 147> add replicat rtag , integrated, exttrail ./dirdat/tc

REPLICAT (Integrated) added.


GGSCI (misha2 as tc_ogg_replicat@db2) 148> start extract etag

Sending START request to MANAGER ...
EXTRACT ETAG starting


GGSCI (misha2 as tc_ogg_replicat@db2) 149> start replicat rtag

Sending START request to MANAGER ...
REPLICAT RTAG starting
I used two databases in my setup - db1 and db2 that I named SOURCE and TARGET in the rest of this blog post for the sake of clarity.
The extract configured to capture the changes from db1 database and write data to trail files.
The replicat reads the trail files and applies the changes into db2 database.
Next I am about to run the following code in the source database from which the extract captures changes:
TC@SOURCE> create sequence t_seq;

Sequence created.

TC@SOURCE> 
TC@SOURCE> create table t(
  2    id  int default t_seq.nextval,
  3    msg varchar2(10))
  4  /

Table created.

TC@SOURCE> 
TC@SOURCE> insert into t(msg) values ('NO TAG');

1 row created.

TC@SOURCE> commit;

Commit complete.

TC@SOURCE> 
TC@SOURCE> exec dbms_streams_adm.set_tag( hextoraw('12'))

PL/SQL procedure successfully completed.

TC@SOURCE> 
TC@SOURCE> insert into t(msg) values ('TAG '||rawtohex(dbms_streams_adm.get_tag));

1 row created.

TC@SOURCE> commit;

Commit complete.

TC@SOURCE> 
TC@SOURCE> exec dbms_streams_adm.set_tag( hextoraw('34'))

PL/SQL procedure successfully completed.

TC@SOURCE> 
TC@SOURCE> insert into t(msg) values ('TAG '||rawtohex(dbms_streams_adm.get_tag));

1 row created.

TC@SOURCE> commit;

Commit complete.

TC@SOURCE> 
TC@SOURCE> exec dbms_streams_adm.set_tag( hextoraw('56'))

PL/SQL procedure successfully completed.

TC@SOURCE> 
TC@SOURCE> insert into t(msg) values ('TAG '||rawtohex(dbms_streams_adm.get_tag));

1 row created.

TC@SOURCE> commit;

Commit complete.
Havind done that, I got next results in SOURCE database:
TC@SOURCE> select * from t;

        ID MSG
---------- ----------
         1 NO TAG
         2 TAG 12
         3 TAG 34
         4 TAG 56
And that in TARGET:
TC@TARGET> select * from t order by id;

        ID MSG
---------- ----------
         1 NO TAG
         2 TAG 12
         4 TAG 56
Notice that an insert of a record with ID=3, MSG="TAG 34" was not replicated because we have filtered out that record on the extract.
That works flawlessly being executed for DML, but it does not work for DDL:
TC@SOURCE> exec dbms_streams_adm.set_tag( hextoraw('12'))

PL/SQL procedure successfully completed.

TC@SOURCE> 
TC@SOURCE> alter table t add tag12 int;

Table altered.

TC@SOURCE> 
TC@SOURCE> exec dbms_streams_adm.set_tag( hextoraw('34'))

PL/SQL procedure successfully completed.

TC@SOURCE> 
TC@SOURCE> alter table t add tag34 int;

Table altered.

TC@SOURCE> describe t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                                 NUMBER(38)
 MSG                                                VARCHAR2(10)
 TAG12                                              NUMBER(38)
 TAG34                                              NUMBER(38)
I have added two columns to the table and the second one, TAG34, should not have been replicated.
But in fact, both of the commands were replicated:
TC@TARGET> describe t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                                 NUMBER(38)
 MSG                                                VARCHAR2(10)
 TAG12                                              NUMBER(38)
 TAG34                                              NUMBER(38)
It means that aforementioned MOS document How to exclude ddl in IE (integrated extract) issued from a specific user? (Doc ID 2107293.1) does not work in that configuration which, by the way, I think is quite typical.
Luckily, Oracle Support published another note after we concluded our work on the SR: EXCLUDETAG Issue With Integrated Extract While Excluding DDL For Specific User (Doc ID 2185538.1).
We need to put the following line into an extract parameter file to get it working: tranlogoptions _dbfilterddl
GGSCI (misha2 as tc_ogg_replicat@db2) 168> view params etag

EXTRACT ETAG

USERIDALIAS db1_tc_ogg_extract
TRANLOGOPTIONS EXCLUDETAG 34
TRANLOGOPTIONS _dbfilterddl
LOGALLSUPCOLS
EXTTRAIL ./dirdat/tc
DDL INCLUDE ALL

TABLE TC.*;
Having added that, the same code adding two columns worked as it should:
TC@SOURCE> describe t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                                 NUMBER(38)
 MSG                                                VARCHAR2(10)
 TAG12                                              NUMBER(38)
 TAG34                                              NUMBER(38)

TC@TARGET> describe t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                                 NUMBER(38)
 MSG                                                VARCHAR2(10)
 TAG12                                              NUMBER(38)
It means that TAG34 column, being added with tag 34, was not replicated.
There is also a new line in a ggserr.log file that was not present when I started the extract without _dbfilterddl:
Logmining server DDL filtering enabled.
2016-10-31 13:54:33  INFO    OGG-00963  Oracle GoldenGate Manager for Oracle, mgr.prm:  Command received from GGSCI on host [172.16.113.245]:57437 (START EXTRACT ETAG ).
2016-10-31 13:54:33  INFO    OGG-00960  Oracle GoldenGate Manager for Oracle, mgr.prm:  Access granted (rule #6).
2016-10-31 13:54:34  INFO    OGG-00992  Oracle GoldenGate Capture for Oracle, etag.prm:  EXTRACT ETAG starting.
2016-10-31 13:54:34  INFO    OGG-03059  Oracle GoldenGate Capture for Oracle, etag.prm:  Operating system character set identified as UTF-8.
2016-10-31 13:54:34  INFO    OGG-02695  Oracle GoldenGate Capture for Oracle, etag.prm:  ANSI SQL parameter syntax is used for parameter parsing.
2016-10-31 13:54:38  INFO    OGG-03522  Oracle GoldenGate Capture for Oracle, etag.prm:  Setting session time zone to source database time zone 'GMT'.
2016-10-31 13:54:38  WARNING OGG-04033  Oracle GoldenGate Capture for Oracle, etag.prm:   LOGALLSUPCOLS has set the NOCOMPRESSDELETES and GETUPDATEBEFORES parameters on.
2016-10-31 13:54:38  INFO    OGG-01815  Oracle GoldenGate Capture for Oracle, etag.prm:  Virtual Memory Facilities for: BR
    anon alloc: mmap(MAP_ANON)  anon free: munmap
    file alloc: mmap(MAP_SHARED)  file free: munmap
    target directories:
    /u01/app/oracle/12.2.0.1/ggs/BR/ETAG.
2016-10-31 13:54:38  INFO    OGG-01851  Oracle GoldenGate Capture for Oracle, etag.prm:  filecaching started: thread ID: 140594853938944.
2016-10-31 13:54:38  INFO    OGG-00975  Oracle GoldenGate Manager for Oracle, mgr.prm:  EXTRACT ETAG starting.
2016-10-31 13:54:38  INFO    OGG-01815  Oracle GoldenGate Capture for Oracle, etag.prm:  Virtual Memory Facilities for: COM
    anon alloc: mmap(MAP_ANON)  anon free: munmap
    file alloc: mmap(MAP_SHARED)  file free: munmap
    target directories:
    /u01/app/oracle/12.2.0.1/ggs/dirtmp.
2016-10-31 13:54:58  WARNING OGG-02045  Oracle GoldenGate Capture for Oracle, etag.prm:  Database does not have streams_pool_size initialization parameter configured.
2016-10-31 13:54:59  INFO    OGG-02248  Oracle GoldenGate Capture for Oracle, etag.prm:  Logmining server DDL filtering enabled.
2016-10-31 13:55:08  INFO    OGG-02068  Oracle GoldenGate Capture for Oracle, etag.prm:  Integrated capture successfully attached to logmining server OGG$CAP_ETAG using OGGCapture API.
2016-10-31 13:55:08  INFO    OGG-02089  Oracle GoldenGate Capture for Oracle, etag.prm:  Source redo compatibility version is: 12.1.0.2.0.
2016-10-31 13:55:08  INFO    OGG-02086  Oracle GoldenGate Capture for Oracle, etag.prm:  Integrated Dictionary will be used.
2016-10-31 13:55:09  WARNING OGG-02905  Oracle GoldenGate Capture for Oracle, etag.prm:  Replication of OID column in object tables may diverge.
2016-10-31 13:55:09  INFO    OGG-00993  Oracle GoldenGate Capture for Oracle, etag.prm:  EXTRACT ETAG started.

tl;dr

EXCLUDETAG parameter does not work when a tag is set using DBMS_STREAMS_ADM.SET_TAG in OGG 12.2.0.1.160823
. It prevents replicating only DML commands and does not restrict DDL commands from being replicated.
We could use underscope parameter _dbfilterddl in the extract parameter file like in the following line:
TRANLOGOPTIONS _dbfilterddl
This way we may restrict both DDL and DML commands from being replicated when the appropriate tag is set (or any in case we use "EXCLUDETAG +" in the extract parameter file).

четверг, 1 сентября 2016 г.

Incorrect handling of unused columns during GoldenGate initial load using Direct Bulk Load to SQL*Loader

Today I have faced an interesting issue performing GoldenGate initial-load with a Direct Bulk Load to SQL*Loader.
The issue was related to incorrect handling of unused columns during such loads and can cause incorrect data getting loaded into a database under certain conditions.
I tried to reproduce the issue using one database in my setup but wasn't able to do that and stuck with two databases.
I used Oracle Database 12.1.0.2 with DBBP 12.1.0.2.160419 applied and GoldenGate 12.2.0.1.160419 Patch Set.
I'm going to use two database in my test case, let it be SOURCE and TARGET. Firstly, let's create a test schema with a single table populating it with some data:
DBA@SOURCE> grant create session, create table, unlimited tablespace to tc identified by tc;

Grant succeeded.

DBA@SOURCE> conn tc/tc@source
Connected.
TC@SOURCE> 
SQL> create table direct_load(
  2    column1 int,
  3    unused1 int,
  4    column2 int,
  5    column3 int);

Table created.

TC@SOURCE> 
TC@SOURCE> insert into direct_load values (1, 0, 2, 3);

1 row created.

TC@SOURCE> commit;

Commit complete.

TC@SOURCE> select *
  2    from direct_load;

   COLUMN1    UNUSED1    COLUMN2    COLUMN3
---------- ---------- ---------- ----------
         1          0          2          3
Secondly, we are making column "unused1" unused:
TC@SOURCE> alter table direct_load set unused column unused1;

Table altered.
Now I will execute the same DDL commands into TARGET database, which is obviously what GoldenGate server will do if we setup DDL replication:
DBA@TARGET> grant create session, create table, unlimited tablespace to tc identified by tc;

Grant succeeded.

DBA@TARGET> conn tc/tc@target
Connected.
TC@TARGET> create table direct_load(
  2    column1 int,
  3    unused1 int,
  4    column2 int,
  5    column3 int);

Table created.

TC@TARGET> 
TC@TARGET> insert into direct_load values (1, 0, 2, 3);

1 row created.

TC@TARGET> commit;

Commit complete.

TC@TARGET> select *
  2    from direct_load;

   COLUMN1    UNUSED1    COLUMN2    COLUMN3
---------- ---------- ---------- ----------
         1          0          2          3

TC@TARGET>
TC@TARGET>  alter table direct_load set unused column unused1;
Consider the scenario when we have decided to resynchronize data in TARGET database from SOURCE database.
I'm going to remove all rows from the table in TARGET database first:
TC@TARGET> truncate table direct_load;

Table truncated.
Proceed with GoldenGate setup. I want to use GoldenGate Direct Bulk Load to SQL*Loader capability to move the data from SOURCE database to TARGET database.
Let's use IRTC - as a name of the initial load replicat and IETC - as a name of the initial load extract:
-- setup initial load replicat
GGSCI (ogg-test) 2> edit params irtc
-- UserIdAlias may be customized accordingly to your environment or simply use a username/password pair
Replicat irtc
AssumeTargetDefs
UserIdAlias target_ogg_replicat
DiscardFile ./dirrpt/irtc.dsc, Purge
-- I want to use Direct Bulk Load
BulkLoad
-- I used PDB "orcl" for my experiments:
Map orcl.tc.*, target orcl.tc.*;

-- setup initial load extract
GGSCI (ogg-test) 149> edit params ietc
Extract ietc
RmtHost ogg-test, MgrPort 7809
RmtTask Replicat, Group irtc
UserIdAlias source_ogg_extract
Table orcl.tc.direct_load;

-- let's create GoldenGate processes
GGSCI (ogg-test) 116> add replicat irtc SpecialRun

REPLICAT added.

GGSCI (ogg-test) 117> add extract ietc SourceIsTable

EXTRACT added.
The setup has been finished. Right now we can try to perform initial load:
GGSCI (ogg-test) 15> info i*tc, tasks

EXTRACT    IETC      Initialized   2016-09-01 08:54   Status STOPPED
Checkpoint Lag       Not Available
Log Read Checkpoint  Not Available
                     First Record         Record 0
Task                 SOURCEISTABLE

REPLICAT   IRTC      Initialized   2016-09-01 08:54   Status STOPPED
Checkpoint Lag       00:00:00 (updated 00:00:28 ago)
Log Read Checkpoint  Not Available
Task                 SPECIALRUN

GGSCI (ogg-test) 16> start extract ietc

Sending START request to MANAGER ...
EXTRACT IETC starting
The load has completed pretty soon:
GGSCI (ogg-test) 17> info i*tc, tasks

EXTRACT    IETC      Last Started 2016-09-01 08:55   Status STOPPED
Checkpoint Lag       Not Available
Log Read Checkpoint  Table ORCL.TC.DIRECT_LOAD
                     2016-09-01 08:55:27  Record 1
Task                 SOURCEISTABLE

REPLICAT   IRTC      Initialized   2016-09-01 08:54   Status STOPPED
Checkpoint Lag       00:00:00 (updated 00:02:09 ago)
Log Read Checkpoint  Not Available
Task                 SPECIALRUN
ggserr.log file doesn't raise any concerns:
2016-09-01 08:55:11  INFO    OGG-00987  Oracle GoldenGate Command Interpreter for Oracle:  GGSCI command (velikikh): start extract ietc.
2016-09-01 08:55:11  INFO    OGG-00963  Oracle GoldenGate Manager for Oracle, mgr.prm:  Command received from GGSCI on host [127.0.0.1]:54562 (START EXTRACT IETC ).
2016-09-01 08:55:11  INFO    OGG-00960  Oracle GoldenGate Manager for Oracle, mgr.prm:  Access granted (rule #8).
2016-09-01 08:55:11  INFO    OGG-01017  Oracle GoldenGate Capture for Oracle, ietc.prm:  Wildcard resolution set to IMMEDIATE because SOURCEISTABLE is used.
2016-09-01 08:55:11  INFO    OGG-00992  Oracle GoldenGate Capture for Oracle, ietc.prm:  EXTRACT IETC starting.
2016-09-01 08:55:11  INFO    OGG-03059  Oracle GoldenGate Capture for Oracle, ietc.prm:  Operating system character set identified as ISO-8859-1.
2016-09-01 08:55:11  INFO    OGG-02695  Oracle GoldenGate Capture for Oracle, ietc.prm:  ANSI SQL parameter syntax is used for parameter parsing.
2016-09-01 08:55:12  INFO    OGG-03522  Oracle GoldenGate Capture for Oracle, ietc.prm:  Setting session time zone to source database time zone 'GMT'.
2016-09-01 08:55:12  WARNING OGG-00752  Oracle GoldenGate Capture for Oracle, ietc.prm:  Failed to validate table ORCL.TC.DIRECT_LOAD. Likely due to existence of unused column. Please make sure you use sourcedefs in downstream Replicat, or the target table has exactly the same unused columns when using ASSUMETARGETDEFS or DDL replication.
2016-09-01 08:55:12  WARNING OGG-06439  Oracle GoldenGate Capture for Oracle, ietc.prm:  No unique key is defined for table DIRECT_LOAD. All viable columns will be used to represent the key, but may not guarantee uniqueness. KEYCOLS may be used to define the key.
2016-09-01 08:55:12  INFO    OGG-06509  Oracle GoldenGate Capture for Oracle, ietc.prm:  Using the following key columns for source table ORCL.TC.DIRECT_LOAD: COLUMN1, COLUMN2, COLUMN3.
2016-09-01 08:55:12  INFO    OGG-01851  Oracle GoldenGate Capture for Oracle, ietc.prm:  filecaching started: thread ID: 7.
2016-09-01 08:55:12  INFO    OGG-01815  Oracle GoldenGate Capture for Oracle, ietc.prm:  Virtual Memory Facilities for: COM
    anon alloc: mmap(MAP_ANON)  anon free: munmap
    file alloc: mmap(MAP_SHARED)  file free: munmap
    target directories:
    /export/home/velikikh/oracle/12.2.0.1/ggs/dirtmp.
2016-09-01 08:55:16  INFO    OGG-00975  Oracle GoldenGate Manager for Oracle, mgr.prm:  EXTRACT IETC starting.
2016-09-01 08:55:16  INFO    OGG-00993  Oracle GoldenGate Capture for Oracle, ietc.prm:  EXTRACT IETC started.
2016-09-01 08:55:16  INFO    OGG-00963  Oracle GoldenGate Manager for Oracle, mgr.prm:  Command received from EXTRACT on host [127.0.0.1]:58728 (START REPLICAT IRTC CPU -1 PRI -1 PARAMS ).
2016-09-01 08:55:16  INFO    OGG-00960  Oracle GoldenGate Manager for Oracle, mgr.prm:  Access granted (rule #8).
2016-09-01 08:55:16  INFO    OGG-01025  Oracle GoldenGate Delivery for Oracle:  REPLICAT task started by manager (port 7810).
2016-09-01 08:55:21  INFO    OGG-00963  Oracle GoldenGate Manager for Oracle, mgr.prm:  Command received from RMTTASK on host [::1]:59974 (REPORT 3871 7810).
2016-09-01 08:55:21  INFO    OGG-00960  Oracle GoldenGate Manager for Oracle, mgr.prm:  Access granted (rule #1).
2016-09-01 08:55:21  INFO    OGG-00973  Oracle GoldenGate Manager for Oracle, mgr.prm:  Manager started replicat task process (Port 7810).
2016-09-01 08:55:26  INFO    OGG-01229  Oracle GoldenGate Delivery for Oracle:  Connected to ogg-test:50783.
2016-09-01 08:55:26  INFO    OGG-00995  Oracle GoldenGate Delivery for Oracle, irtc.prm:  REPLICAT IRTC starting.
2016-09-01 08:55:26  INFO    OGG-03059  Oracle GoldenGate Delivery for Oracle, irtc.prm:  Operating system character set identified as ISO-8859-1.
2016-09-01 08:55:26  INFO    OGG-02695  Oracle GoldenGate Delivery for Oracle, irtc.prm:  ANSI SQL parameter syntax is used for parameter parsing.
2016-09-01 08:55:27  INFO    OGG-02679  Oracle GoldenGate Delivery for Oracle, irtc.prm:  The Replicat process logged on to database ORCL and can only apply to that database.
2016-09-01 08:55:27  INFO    OGG-06451  Oracle GoldenGate Delivery for Oracle, irtc.prm:  Triggers will be suppressed by default.
2016-09-01 08:55:27  INFO    OGG-01815  Oracle GoldenGate Delivery for Oracle, irtc.prm:  Virtual Memory Facilities for: COM
    anon alloc: mmap(MAP_ANON)  anon free: munmap
    file alloc: mmap(MAP_SHARED)  file free: munmap
    target directories:
    /export/home/velikikh/oracle/12.2.0.1/ggs/dirtmp.
2016-09-01 08:55:27  INFO    OGG-00996  Oracle GoldenGate Delivery for Oracle, irtc.prm:  REPLICAT IRTC started.
2016-09-01 08:55:27  INFO    OGG-03522  Oracle GoldenGate Delivery for Oracle, irtc.prm:  Setting session time zone to source database time zone 'GMT'.
2016-09-01 08:55:27  INFO    OGG-02911  Oracle GoldenGate Capture for Oracle, ietc.prm:  Processing table ORCL.TC.DIRECT_LOAD.
2016-09-01 08:55:27  INFO    OGG-06495  Oracle GoldenGate Capture for Oracle, ietc.prm:  OGG created a session pool with SESSIONPOOLMAX 10, SESSIONPOOLMIN 2 and SESSIONPOOLINCR 2.
2016-09-01 08:55:27  WARNING OGG-02760  Oracle GoldenGate Delivery for Oracle, irtc.prm:  ASSUMETARGETDEFS is ignored because trail file  contains table definitions.
2016-09-01 08:55:27  INFO    OGG-06506  Oracle GoldenGate Delivery for Oracle, irtc.prm:  Wildcard MAP resolved (entry orcl.tc.*): Map "ORCL"."TC"."DIRECT_LOAD", target orcl.tc."DIRECT_LOAD".
2016-09-01 08:55:28  WARNING OGG-06439  Oracle GoldenGate Delivery for Oracle, irtc.prm:  No unique key is defined for table DIRECT_LOAD. All viable columns will be used to represent the key, but may not guarantee uniqueness. KEYCOLS may be used to define the key.
2016-09-01 08:55:28  INFO    OGG-02756  Oracle GoldenGate Delivery for Oracle, irtc.prm:  The definition for table ORCL.TC.DIRECT_LOAD is obtained from the trail file.
2016-09-01 08:55:28  INFO    OGG-06511  Oracle GoldenGate Delivery for Oracle, irtc.prm:  Using following columns in default map by name: COLUMN1, COLUMN2, COLUMN3.
2016-09-01 08:55:28  INFO    OGG-06510  Oracle GoldenGate Delivery for Oracle, irtc.prm:  Using the following key columns for target table ORCL.TC.DIRECT_LOAD: COLUMN1, COLUMN2, COLUMN3.
2016-09-01 08:55:28  INFO    OGG-00178  Oracle GoldenGate Delivery for Oracle, irtc.prm:  owner = "TC", table = "DIRECT_LOAD".
2016-09-01 08:55:28  INFO    OGG-00991  Oracle GoldenGate Capture for Oracle, ietc.prm:  EXTRACT IETC stopped normally.
2016-09-01 08:55:33  INFO    OGG-00994  Oracle GoldenGate Delivery for Oracle, irtc.prm:  REPLICAT IRTC stopped normally.
Yes, I had a warning like this:
2016-09-01 08:55:12  WARNING OGG-00752  Oracle GoldenGate Capture for Oracle, ietc.prm:  Failed to validate table ORCL.TC.DIRECT_LOAD. Likely due to existence of unused column. Please make sure you use sourcedefs in downstream Replicat, or the target table has exactly the same unused columns when using ASSUMETARGETDEFS or DDL replication.
But I obviously didn't violate it as the source and the target table has the same unused columns.
Let's see what has loaded into TARGET database:
TC@TARGET> select * from direct_load;

   COLUMN1    COLUMN2    COLUMN3
---------- ---------- ----------
         1                     2
That's not I wanted to load and that differs from the original table in SOURCE database:
TC@SOURCE> select * from direct_load;

   COLUMN1    COLUMN2    COLUMN3
---------- ---------- ----------
         1          2          3
Clearly the data from COLUMN2 got loaded into COLUMN3 and I didn't get any errors afterwards leaving my database with "wrong" data.
That's the worst scenario. Originally I faced the issue while I was moving a bunch of data from one database to another using GoldenGate.
The load got ABENDED with ORA-01840 as in my case COLUMN3 had a DATE datatype which may be easily reproduced by the following code:
SQL> select to_date('2', 'yyyy-mm-dd hh24:mi:ss') from dual;
select to_date('2', 'yyyy-mm-dd hh24:mi:ss') from dual
               *
ERROR at line 1:
ORA-01840: input value not long enough for date format
Of course, if you are lucky, you may get some other datatype conversion errors. If you are not, you may end with wrong data in your database and it may take a while before you find the problem.
A few words about workarounds.
In this simple scenario, which I have written just for demonstration purposes, it was enough to drop unused columns in TARGET database:
TC@TARGET> alter table direct_load drop unused columns;

Table altered.