1.54. failednode( integer, integer )

関数特性

言語: PLPGSQL

戻り値: integer

failed_node to backup_node からフェイルオーバを開始します。この関数は全てのノードで呼ばれなければならず、そして全てのノードのデーモンの再起動に待機しなければなりません。

declare
	p_failed_node		alias for $1;
	p_backup_node		alias for $2;
	v_row			record;
	v_row2			record;
	v_n			int4;
begin
	-- ----
	-- 中枢構成にロックの取得
	-- ----
	lock table sl_config_lock;

	-- ----
	-- 全ての一貫性の検査が第一
	-- 故障ノードへの経路を全てのシステムが所有しているかの検査
	-- 同時にバックアップノードへの経路があるかも検査
	-- ----
	for v_row in select P.pa_client
			from sl_path P
			where P.pa_server = p_failed_node
				and P.pa_client <> p_backup_node
				and not exists (select true from sl_path PP
							where PP.pa_server = p_backup_node
								and PP.pa_client = P.pa_client)
	loop
		raise exception 'Slony-I: cannot failover - node % has no path to the backup node',
				v_row.pa_client;
	end loop;

	-- ----
	-- Check all sets originating on the failed node
	-- ----
	for v_row in select set_id
			from sl_set
			where set_origin = p_failed_node
	loop
		-- ----
		-- 故障ノードのオリジンである全てのセットで
		-- バックアップノードが購読されているかの検査
		-- ----
		select into v_row2 sub_forward, sub_active
				from sl_subscribe
				where sub_set = v_row.set_id
					and sub_receiver = p_backup_node;
		if not found then
			raise exception 'Slony-I: cannot failover - node % is not subscribed to set %',
					p_backup_node, v_row.set_id;
		end if;

		-- ----
		-- 購読動作が有効かの検査
		-- ----
		if not v_row2.sub_active then
			raise exception 'Slony-I: cannot failover - subscription for set % is not active',
					v_row.set_id;
		end if;

		-- ----
		-- 他に購読ノードがあれば、バックアップノードは
		-- 発信ノードになる必要も同時にあります。
		-- ----
		select into v_n count(*)
				from sl_subscribe
				where sub_set = v_row.set_id
					and sub_receiver <> p_backup_node;
		if v_n > 0 and not v_row2.sub_forward then
			raise exception 'Slony-I: cannot failover - node % is not a forwarder of set %',
					p_backup_node, v_row.set_id;
		end if;
	end loop;

	-- ----
	-- 厳しく故障ノードの全ての接続を停止
	-- ----
	perform terminateNodeConnections(
			'_schemadoc_Node_' || p_failed_node);

-- 
   RebuildListenEntries() が有効になっていれば以下のコードは使われなくなる
-- 
   事を覚えておいてください。
if false then
	-- ----
	-- 故障ノードから何かを監視している全てのノードに
	-- 代わりとしてバックアップノード上のそれらを監視するようにします。
	-- ----
	for v_row in select * from sl_listen
			where li_provider = p_failed_node
				and li_receiver <> p_backup_node
	loop
		perform storeListen_int(v_row.li_origin,
				p_backup_node, v_row.li_receiver);
	end loop;

	-- ----
	-- 故障ノードが監視していた全ての事象を
	-- バックアップノードが監視する様にします。
	-- ----
	for v_row in select li_origin, li_provider
			from sl_listen
			where li_receiver = p_failed_node
				and li_provider <> p_backup_node
	loop
		perform storeListen_int(v_row.li_origin,
				v_row.li_provider, p_backup_node);
	end loop;

	-- ----
	-- 故障ノードから何んでも受け取る
	-- 全ての sl_listen エントリを削除します。
	-- ----
	delete from sl_listen
			where li_provider = p_failed_node
				or li_receiver = p_failed_node;
end if;

	-- ----
	-- セットの移動
	-- ----
	for v_row in select S.set_id, (select count(*)
					from sl_subscribe SUB
					where S.set_id = SUB.sub_set
						and SUB.sub_receiver <> p_backup_node
						and SUB.sub_provider = p_failed_node)
					as num_direct_receivers 
			from sl_set S
			where S.set_origin = p_failed_node
			for update
	loop
		-- ----
		-- バックアップノードが唯一の直接購読ノードか否か。
		-- ----
		if v_row.num_direct_receivers = 0 then
raise notice 'failedNode: set % has no other direct receivers - move now', v_row.set_id;
			-- ----
			-- バックアップノードは唯一の直接購読ノードで、セットのすぐに移動します。
			-- バックアップノード上でそれ自身は、次の作業をします。
			-- 全てのユーザモードのトリガーのリストア
			-- ログトリガーの追加、購読の削除、および
			-- 旧くなった setsync ステータスの削除
			-- ----
			if p_backup_node = getLocalNodeId('_schemadoc') then
				for v_row2 in select * from sl_table
						where tab_set = v_row.set_id
				loop
					perform alterTableRestore(v_row2.tab_id);
				end loop;
			end if;

			update sl_set set set_origin = p_backup_node
					where set_id = v_row.set_id;

			if p_backup_node = getLocalNodeId('_schemadoc') then
				delete from sl_setsync
						where ssy_setid = v_row.set_id;

				for v_row2 in select * from sl_table
						where tab_set = v_row.set_id
				loop
					perform alterTableForReplication(v_row2.tab_id);
				end loop;
			end if;

			delete from sl_subscribe
					where sub_set = v_row.set_id
						and sub_receiver = p_backup_node;
		else
raise notice 'failedNode: set % has other direct receivers - change providers only', v_row.set_id;
			-- ----
			-- バックアップノードは唯一の直接購読ノードではありません。
			-- 意味する所は、この時点で全ての直接購読ノードをバックアップノードから
			-- 受け取るようにリダイレクトし、バックアップノードそれ自身は
			-- 他から受け取るようにします。
			-- 管理ユーティリティは slon エンジンが再起動するまで待機し、
			-- そのあと最高位の SYNC を所有しているノード上で failedNode2()
			-- を呼び出し、そしてこれを後でバックアップノード上に
			-- リダイレクトします。
			-- ----
			update sl_subscribe
					set sub_provider = (select min(SS.sub_receiver)
							from sl_subscribe SS
							where SS.sub_set = v_row.set_id
								and SS.sub_provider = p_failed_node
								and SS.sub_receiver <> p_backup_node
								and SS.sub_forward)
					where sub_set = v_row.set_id
						and sub_receiver = p_backup_node;
			update sl_subscribe
					set sub_provider = p_backup_node
					where sub_set = v_row.set_id
						and sub_provider = p_failed_node
						and sub_receiver <> p_backup_node;
		end if;
	end loop;

	-- sl_listen テーブルの書き換え
	perform RebuildListenEntries();

	-- ----
	-- ノードデーモンの再起動の確認
	-- ----
	notify "_schemadoc_Restart";

	-- ----
	-- とりあえずこれでお終い
	-- ----
	return p_failed_node;
end;