Ян. 062009
 

В началото на годината ще ви запозная с една мотика, от която много боли. Някои фирми (примерно Стемо) използват т.нар. basic replication за репликация на данни между Oracle сървъри. Ние го правим от 1999, когато с Oracle 8i (8.1.5) успяхме да преточим 10 таблици между 1 master site и 9 snapshot sites през едни много разпадащи се връзки с модеми. За времето си това решение беше идеално Още тогава успяхме да докараме 15 минути време за репликиране на данните в едната посока (когато има връзка). Сега, близо 10 години и 3 версии на Oracle по-късно, това вече е legacy система, но все още търкаля (и ще продължава да търкаля дълги години) с ~1800 materialized views между 1 master и 12 mview сървъра.

С две думи, ако ви кажа, че съм много дълго съм сърбал попарата на всякакви mviews, може да ми вярвате. Аз се занимавах с тази система около 7 години. От началото на 2006 един колега постепенно пое товара от моите плещи за няма и година навлезе толкова добре, че започна да се справя съвсем сам. А работата хич не е лека – това… хммм… абе нека кажем, че не е най-съвършения механизъм в Oracle Database.

В теорията на базите от данни, замисъла на тези snapshots е да копират данни от mater към snapshot таблица. Естествено Oracle ги натоварват с още много функционалност (може би за това от 9i ги прекръстиха в mviews). Всеки е чувал за query rewrite механизмите, дъвчат се из целия нет. Но един друг не по-малко вълнуващ механизъм е копирането на данни в двете посоки. Идеята е следната: има един master сайт, в който стоят данните. Има един (или повече) snapshot сайт(ове), на които има цяло или частично копие на тези данни. Това копие се опреснява през определен период от master таблица с т.нар. refresh (няма да изпадам в подробности за complete и fast refresh). До тук имаме едно копие на данните, което е read-only. Ако ни е обхванал дълбок мазохизъм (или сме притиснати от обстоятелствата), можем да дефинираме че едно mview е updateable. Това означава, че освен че изтегля данни от master таблицата, то може да приема и DML. Всички DML заявки към Updateable MViews се събират в една опашка и периодично се изпращат към master сайта с механизъм, известен като push. Клопката тук е, че докато опресняването (refresh) от master към mview става mview по mvew (може да се прави и на групи), то в обратна посока (push) върви поток от транзакции, аналогичен на този при Advanced replication.

И тук идва времето за днешното бъгче. Понякога (обикновено по време на създаване) едно mview може да стане не updateable, а writeable. Това означава, че всички DML операции си работят върху него (не е read-only), но транзакциите не се прихващат и не се изпращат към master сайта. При следващия refresh данните, въведени върху mview-то изчезват, защото refresh-а синхронизира данните е mvew с тези в master таблицата. А там новите данни ги няма.

Първия път като ми се случи това се стреснах много силно. Това си е жива загуба на данни! И тъжното е, че на пръв поглед е много трудно да се идентифицира едно такова mview. Ако имаш едно и искаш да го пробваш е лесно – правиш някакъв dml и гледаш дали се прихваща в таблицата deftrandest. Но в една жива система с 12 mvew сървъра, на всеки от които има ~1800 mviews нещата загрубяват. dba_mviews не подсказва нищо, нито има някаква грешка в dba_objects.

Ето и решението:

select d.owner, object_name, trigflag, mv.compile_state, mv.updatable
from dba_objects d, sys.tab$ t, dba_mviews mv
where d.object_id = t.obj#
and mv.mview_name = d.object_name
and mv.owner = d.owner
and mv.updatable = 'Y';

Тази подла заявка ни показва една колонка trigflag, по която можем да идентифицираме какви internаl triggers има таблицата нa едно mview. Тази колонка е побитова маска със следните стойности:
0x01 => deferred RPC queue
0x02 => mview log
0x04 => updateable mview log

С други думи едно updateable mview трябва да има стойност 5 (или 7, ако само по себе си е и master за друго mview). Проблемните (writeable) mviews имат стойност 4 (или 6), т.е. това mview позволява updates, но не ги enqueue-ва като defcalls (леле какво изречение!).

След като идентифицираме такова mview, тригера може да се създаде със следната секретна процедура:
exec dbms_internal_trigger.make(owner, mview_name, 1)

Убеден съм, че това ще помогне на някого!

NB! Това не е документирано официално. Тествано и работи на 10.1 и 10.2. Използваме го в production от години. Но гаранция за бъдещите версии няма.

 Posted by at 12:41