You can subscribe to this list here.
2010 |
Jan
|
Feb
|
Mar
|
Apr
(4) |
May
(28) |
Jun
(12) |
Jul
(11) |
Aug
(12) |
Sep
(5) |
Oct
(19) |
Nov
(14) |
Dec
(12) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2011 |
Jan
(18) |
Feb
(30) |
Mar
(115) |
Apr
(89) |
May
(50) |
Jun
(44) |
Jul
(22) |
Aug
(13) |
Sep
(11) |
Oct
(30) |
Nov
(28) |
Dec
(39) |
2012 |
Jan
(38) |
Feb
(18) |
Mar
(43) |
Apr
(91) |
May
(108) |
Jun
(46) |
Jul
(37) |
Aug
(44) |
Sep
(33) |
Oct
(29) |
Nov
(36) |
Dec
(15) |
2013 |
Jan
(35) |
Feb
(611) |
Mar
(5) |
Apr
(55) |
May
(30) |
Jun
(28) |
Jul
(458) |
Aug
(34) |
Sep
(9) |
Oct
(39) |
Nov
(22) |
Dec
(32) |
2014 |
Jan
(16) |
Feb
(16) |
Mar
(42) |
Apr
(179) |
May
(7) |
Jun
(6) |
Jul
(9) |
Aug
|
Sep
(4) |
Oct
|
Nov
(3) |
Dec
|
2015 |
Jan
|
Feb
|
Mar
|
Apr
(2) |
May
(4) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
|
|
1
(3) |
2
(5) |
3
|
4
(4) |
5
|
6
|
7
(7) |
8
(10) |
9
(6) |
10
(5) |
11
(1) |
12
|
13
|
14
|
15
|
16
|
17
(4) |
18
(1) |
19
|
20
|
21
(5) |
22
(15) |
23
(18) |
24
(7) |
25
(4) |
26
|
27
|
28
(3) |
29
(2) |
30
(11) |
31
(4) |
|
|
From: Michael P. <mic...@us...> - 2011-03-30 16:15:12
|
Project "Postgres-XC". The branch, master has been updated via b9c601ba5509e9c90f1d4d184a514ce47ed7397d (commit) from 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 (commit) - Log ----------------------------------------------------------------- commit b9c601ba5509e9c90f1d4d184a514ce47ed7397d Author: Michael P <mic...@us...> Date: Thu Mar 31 01:12:15 2011 +0900 Fix for bug 3160456: sequence error values If an error occured on GTM for currval or nextval, the incorrect value EINVAL (22) was returned to Postgres-XC node. diff --git a/src/gtm/main/gtm_seq.c b/src/gtm/main/gtm_seq.c index f6c4a21..6a863d2 100644 --- a/src/gtm/main/gtm_seq.c +++ b/src/gtm/main/gtm_seq.c @@ -657,7 +657,7 @@ GTM_SeqGetCurrent(GTM_SequenceKey seqkey) ereport(LOG, (EINVAL, errmsg("The sequence with the given key does not exist"))); - return EINVAL; + return InvalidSequenceValue; } GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE); @@ -719,7 +719,7 @@ GTM_SeqGetNext(GTM_SequenceKey seqkey) ereport(LOG, (EINVAL, errmsg("The sequence with the given key does not exist"))); - return EINVAL; + return InvalidSequenceValue; } GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE); ----------------------------------------------------------------------- Summary of changes: src/gtm/main/gtm_seq.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) hooks/post-receive -- Postgres-XC |
From: Michael P. <mic...@us...> - 2011-03-30 15:32:00
|
Project "Postgres-XC". The branch, merge_postgres_9_0_3 has been deleted was 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 ----------------------------------------------------------------------- 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 Fix GXID feed issue ----------------------------------------------------------------------- hooks/post-receive -- Postgres-XC |
From: Michael P. <mic...@us...> - 2011-03-30 15:30:16
|
Project "Postgres-XC". The branch, REL0_9_4_STABLE has been created at 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 (commit) - Log ----------------------------------------------------------------- ----------------------------------------------------------------------- hooks/post-receive -- Postgres-XC |
From: Michael P. <mic...@us...> - 2011-03-30 15:28:53
|
Project "Postgres-XC". The annotated tag, v0.9.4 has been created at 3ac5a29e291e47af2ba420685c6776012a26779c (tag) tagging 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 (commit) replaces v0.9.3 tagged by Michael P on Thu Mar 31 00:27:51 2011 +0900 - Log ----------------------------------------------------------------- Postgres-XC version 0.9.4 tag Abbas (16): Block creation of concurrent indices To avoid a crash caused by an insert select statement in vacuum.sql To fix a server crash in aggregates as reported in ID 3125430 GetRelationLocInfo can return NULL, this patch adds check for a NULL return at missing places Block FK Constraints and add some missing checks The test case portals finds a NULL connecction in ExecEndRemoteQuery and crashes the server. This patch puts a NULL check to avoid the crash Fixing a few expected output files and changed a warning message to make the output independent of the cluster configuration Add alternate expected output files to take care of regression failures Expected output changed to fix regression failure Add alternate expected output file to fix regression falilure of numeric.sql Fix for server crash 3148037 : does not fix WHERE CURRENT OF, only fixes server crash Fix for server crash as mentioned in bug ID 3170715 Since the creation of unique indices is supposed to fail in XC hence this output is correct This patch fixes a test case in strings.sql This patch fixes the problem in XC that error detail was not being handled. Merge branch 'merge_postgres_9_0_3' of ssh://postgres-xc.git.sourceforge.net/gitroot/postgres-xc/postgres-xc into merge_postgres_9_0_3 Mason Sharp (22): Special hanlding for ANALYZE on the data nodes. Improve performance of "multi-step" queries (an on-going process). Fix bug with primary key in CREATE TABLE statement. Add a message that the RETURNING clause is not yet supported. 1. Support for UPDATE and DELETE WHERE CURRENT OF for Support for COPY SELECT Do not allow the partition column to be updated on hash partitioned Fix some compiler warnings Fix PGXC macro usage Minor cursor changes: Fix for sourceforge.net bug#3013984: Sequence scope Fixed bug where if there are subqueries in the SELECT clause This fixes a couple of issues noticed after the last commit, Add support for INSERT SELECT. Fix a problem when more values are selected than total columns in Fixed a bug with INSERT SELECT when an input value is NULL. Fixed bug in INSERT when omitting a value for the partitioning column. Fixed recently introduced bug with node determination Add support for single-step prepared statements. Added missing #ifdef PGXC Updated regression tests. Block the creation of temp tables until properly supported Michael P (110): Support for CLEAN CONNECTION Block Node Commit on a Datanode. Change Protocol connection between PGXC nodes and GTM/GTM-Proxy. Correction for implicit 2PC Clean up of execRemote.c Improvement of performance with tuple scan Fix for bug 3142311:renaming sequences error Fix for bug 3136262: Improvement of Explicit 2PC error handling Sequence Renaming Node Registering feature Allow only Remote Coordinator to use functionnalities introduced with SQL/MED Fix for partially committed transactions in Implicit 2PC Clean up in Materialize code Fix and Clean up in Executor Base code to support correlated DELETE and UPDATE Support for correlated DELETE for replicated tables The patch implements multiple insert syntax in PGXC. Fix for make -j Fix for bug 3147497 INSERT.. DEFAULT VALUES Fix for replicated tables using multi-INSERT queries. maintenance for 2011, change header files Support for EXECUTE DIRECT CREATE TABLE: new distribution function MODULO Fix for bug 3142430 Fix for bug 3172438: node registration protocol Multiple INSERT support for MODULO tables Fix for bug 3170713: DROP DATABASE wihout cleaning connections Fix for bug 3170708: Default values support for MULTI INSERT Fix for bug 3188711: Fire rules only on Coordinator Block SAVEPOINT because of non-support Fix for bug 3136262: PREPARE information not correctly got Fix for DROP DATABASE Fix for bug 3151626: Support for COPY BINARY Fix for bug 3134395, 3086422, 3136230: 2PC locks Merge with PostgreSQL 9.0.3 Fix for bug 3199029 GTM/GTM-proxy FATAL error handling Merge branch 'master' into merge_postgres_9_0_3 Improve error handling when launching DDL or utilities Merge branch 'master' into merge_postgres_9_0_3 Fix for CREATE INDEX CONCURRENTLY Block PREPARE and EXECUTE for the time being CREATE TABLE default distribution to REPLICATED Change back table defalt type to distributed. Addition of correct output for regress test float4. Fix regression tests for int4 Fix for regression tests float4 Partial fix for regression test float fix Fix for regression test create_table Fix for regression test txid Block trigger as this feature is not supported Partial fix for regression test temp Block FOREIGN constraint creation Block TEMP SEQUENCE and TABLESPACE Fix when transforming a CREATE statement Cleanup of regression files Fix for regression test create_table Fix for regression test returning Fix for regression test inet Block SERIAL sequences Fix for bug 3201711: Sequence view crash Fix for regression test box Fix for bug 3202643: Sequence error Stabilize code for pg_regress tests Fix for bugs 3148479, 3140473: COPY FROM CVS HEADER, COPY TO WITH CSV QUOTE Fix for bug 3141640: non column select Fix for bugs 3124253 and 3202554: Unique remote query node Fix for bug 3205043: pg_dump support for MODULO table Clean up of pgxc_ddl Merge branch 'master' into merge_postgres_9_0_3 Fix for regression test tablespace Fix for regression test point Fix for regression test box Fix for regression test triggers Fix for regression test prepare Fix for regression test case Fix for regression test guc Fix for regression test update Fix for regression test delete Fix for regression test plancache Fix for regression test combocid Fix for regression test copy and point Fix for regression test copyselect Fix for regression test create_misc Fix for regression test create_aggregate Fix for regression test typed_table Fix for regression test sanity_check Fix for regression test select Fix for regression test select_distinct_on Fix for regression test union Fix for regression test transactions Fix for regression test random Fix for regression test hash_index Fix for regression test namespace Fix for regression test portals_p2 Fix for regression test cluster Fix for regression test dependency Fix for regression test rowtypes Fix for regression test returning Fix for regression test select_into Fix for regression test copy2 Fix for regression test truncate Block DEFERRED constraints (DEFERRABLE) Fix for cache lookup bug for type Addition of a check when fetching tuples Fix for regression test create type Fix for regression test privileges Fix for VIEW and SEQUENCE Fix a cache leak WARNING with system cache Fix for bug 3240318: support for lastval Fix GXID feed issue Pavan Deolasee (1): Check for buffer overflow while constructing gtm/gtm_proxy start/stop commands. ----------------------------------------------------------------------- hooks/post-receive -- Postgres-XC |
From: Michael P. <mic...@us...> - 2011-03-30 15:13:26
|
Project "Postgres-XC". The branch, POSTGRES8_4_3_BASE has been created at 490c08dd37fa44af57cd3a2b3e931ef4f3a94853 (commit) - Log ----------------------------------------------------------------- ----------------------------------------------------------------------- hooks/post-receive -- Postgres-XC |
From: Koichi S. <koi...@gm...> - 2011-03-30 12:01:56
|
Thank you Michael; I remember we had similar issue in the past... ---------- Koichi Suzuki 2011/3/30 Michael Paquier <mic...@us...>: > Project "Postgres-XC". > > The branch, merge_postgres_9_0_3 has been updated > via 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 (commit) > from 886f9bbe99120dc751a7d9110521a7cd6cc884d9 (commit) > > > - Log ----------------------------------------------------------------- > commit 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 > Author: Michael P <mic...@us...> > Date: Wed Mar 30 20:25:52 2011 +0900 > > Fix GXID feed issue > > Useless calls to GetCurrentTransactionId were the origin > of consuming extra transaction IDs even on backend Datanode > where it should ot take a transaction ID at some moments. > > This problem may cause node crash, due to same transaction IDs being committed > twice or pooler connection issues. > > diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c > index 0b09093..ed6271e 100644 > --- a/src/backend/access/transam/xact.c > +++ b/src/backend/access/transam/xact.c > @@ -1923,13 +1923,16 @@ CommitTransaction(bool contact_gtm) > bool PrepareLocalCoord = false; > bool PreparePGXCNodes = false; > char implicitgid[256]; > - TransactionId xid = GetCurrentTransactionId(); > + TransactionId xid = InvalidTransactionId; > > if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && contact_gtm) > PreparePGXCNodes = PGXCNodeIsImplicit2PC(&PrepareLocalCoord); > > if (PrepareLocalCoord || PreparePGXCNodes) > + { > + xid = GetCurrentTransactionId(); > sprintf(implicitgid, "T%d", xid); > + } > > /* Save GID where PrepareTransaction can find it again */ > if (PrepareLocalCoord) > > ----------------------------------------------------------------------- > > Summary of changes: > src/backend/access/transam/xact.c | 5 ++++- > 1 files changed, 4 insertions(+), 1 deletions(-) > > > hooks/post-receive > -- > Postgres-XC > > ------------------------------------------------------------------------------ > Enable your software for Intel(R) Active Management Technology to meet the > growing manageability and security demands of your customers. Businesses > are taking advantage of Intel(R) vPro (TM) technology - will your software > be a part of the solution? Download the Intel(R) Manageability Checker > today! http://p.sf.net/sfu/intel-dev2devmar > _______________________________________________ > Postgres-xc-committers mailing list > Pos...@li... > https://lists.sourceforge.net/lists/listinfo/postgres-xc-committers > |
From: Michael P. <mic...@us...> - 2011-03-30 11:28:56
|
Project "Postgres-XC". The branch, merge_postgres_9_0_3 has been updated via 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 (commit) from 886f9bbe99120dc751a7d9110521a7cd6cc884d9 (commit) - Log ----------------------------------------------------------------- commit 46f524e5a6430735c6549afbbe0ea61ab8a4cd49 Author: Michael P <mic...@us...> Date: Wed Mar 30 20:25:52 2011 +0900 Fix GXID feed issue Useless calls to GetCurrentTransactionId were the origin of consuming extra transaction IDs even on backend Datanode where it should ot take a transaction ID at some moments. This problem may cause node crash, due to same transaction IDs being committed twice or pooler connection issues. diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 0b09093..ed6271e 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -1923,13 +1923,16 @@ CommitTransaction(bool contact_gtm) bool PrepareLocalCoord = false; bool PreparePGXCNodes = false; char implicitgid[256]; - TransactionId xid = GetCurrentTransactionId(); + TransactionId xid = InvalidTransactionId; if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && contact_gtm) PreparePGXCNodes = PGXCNodeIsImplicit2PC(&PrepareLocalCoord); if (PrepareLocalCoord || PreparePGXCNodes) + { + xid = GetCurrentTransactionId(); sprintf(implicitgid, "T%d", xid); + } /* Save GID where PrepareTransaction can find it again */ if (PrepareLocalCoord) ----------------------------------------------------------------------- Summary of changes: src/backend/access/transam/xact.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) hooks/post-receive -- Postgres-XC |
From: Koichi S. <koi...@us...> - 2011-03-30 08:40:30
|
Project "Postgres-XC". The branch, ha_support has been updated via 82670fcb1bf768267195f9a9c33d6f9207ba917b (commit) via 073ac85b8c94800eacd9f2d7f8555a8ae9453022 (commit) from 418e9c622429131005817538966cd50d4779e6dd (commit) - Log ----------------------------------------------------------------- commit 82670fcb1bf768267195f9a9c33d6f9207ba917b Author: Koichi Suzuki <koi...@gm...> Date: Wed Mar 30 17:20:45 2011 +0900 Revert "Revert "This is to change the file name of gtm.c at src/pgxc/pgxc_clean to"" This reverts commit 073ac85b8c94800eacd9f2d7f8555a8ae9453022. This is to cancel name change of pgxc_clean to fix build problem. diff --git a/src/pgxc/pgxc_clean/Makefile b/src/pgxc/pgxc_clean/Makefile index 10cba88..5d41e1a 100644 --- a/src/pgxc/pgxc_clean/Makefile +++ b/src/pgxc/pgxc_clean/Makefile @@ -18,7 +18,7 @@ override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) xcm_builddir = $(top_builddir)/src/pgxc/xcm gtm_builddir = $(top_builddir)/src/gtm -OBJS= pgxc_clean.o common.o subfunc.o txnctl.o gtm.o +OBJS= pgxc_clean.o common.o subfunc.o txnctl.o pgxc_clean_gtm.o # Switch when libxcm and libgtmclient are installed via 'make install' # EX_OBJS = $(gtm_builddir)/common/assert.o EX_OBJS = $(gtm_builddir)/common/assert.o \ diff --git a/src/pgxc/pgxc_clean/gtm.c b/src/pgxc/pgxc_clean/pgxc_clean_gtm.c similarity index 100% rename from src/pgxc/pgxc_clean/gtm.c rename to src/pgxc/pgxc_clean/pgxc_clean_gtm.c commit 073ac85b8c94800eacd9f2d7f8555a8ae9453022 Author: Koichi Suzuki <koi...@gm...> Date: Wed Mar 30 17:18:28 2011 +0900 Revert "This is to change the file name of gtm.c at src/pgxc/pgxc_clean to" This reverts commit 418e9c622429131005817538966cd50d4779e6dd. Cmmit reverted to restore to the status before pgxc_clean is added. Found erro r to fix. Sorry. diff --git a/src/pgxc/pgxc_clean/Makefile b/src/pgxc/pgxc_clean/Makefile index 5d41e1a..10cba88 100644 --- a/src/pgxc/pgxc_clean/Makefile +++ b/src/pgxc/pgxc_clean/Makefile @@ -18,7 +18,7 @@ override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) xcm_builddir = $(top_builddir)/src/pgxc/xcm gtm_builddir = $(top_builddir)/src/gtm -OBJS= pgxc_clean.o common.o subfunc.o txnctl.o pgxc_clean_gtm.o +OBJS= pgxc_clean.o common.o subfunc.o txnctl.o gtm.o # Switch when libxcm and libgtmclient are installed via 'make install' # EX_OBJS = $(gtm_builddir)/common/assert.o EX_OBJS = $(gtm_builddir)/common/assert.o \ diff --git a/src/pgxc/pgxc_clean/pgxc_clean_gtm.c b/src/pgxc/pgxc_clean/gtm.c similarity index 100% rename from src/pgxc/pgxc_clean/pgxc_clean_gtm.c rename to src/pgxc/pgxc_clean/gtm.c ----------------------------------------------------------------------- Summary of changes: hooks/post-receive -- Postgres-XC |
From: Koichi S. <koi...@us...> - 2011-03-30 08:04:49
|
Project "Postgres-XC". The branch, ha_support has been updated via 418e9c622429131005817538966cd50d4779e6dd (commit) from dcc82f72fb630f16c965e1e6d3d93b2c6008a150 (commit) - Log ----------------------------------------------------------------- commit 418e9c622429131005817538966cd50d4779e6dd Author: Koichi Suzuki <koi...@gm...> Date: Wed Mar 30 17:03:58 2011 +0900 This is to change the file name of gtm.c at src/pgxc/pgxc_clean to pgxc_clean_gtm.c to avoid confusion. diff --git a/src/pgxc/pgxc_clean/Makefile b/src/pgxc/pgxc_clean/Makefile index 10cba88..5d41e1a 100644 --- a/src/pgxc/pgxc_clean/Makefile +++ b/src/pgxc/pgxc_clean/Makefile @@ -18,7 +18,7 @@ override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) xcm_builddir = $(top_builddir)/src/pgxc/xcm gtm_builddir = $(top_builddir)/src/gtm -OBJS= pgxc_clean.o common.o subfunc.o txnctl.o gtm.o +OBJS= pgxc_clean.o common.o subfunc.o txnctl.o pgxc_clean_gtm.o # Switch when libxcm and libgtmclient are installed via 'make install' # EX_OBJS = $(gtm_builddir)/common/assert.o EX_OBJS = $(gtm_builddir)/common/assert.o \ diff --git a/src/pgxc/pgxc_clean/gtm.c b/src/pgxc/pgxc_clean/pgxc_clean_gtm.c similarity index 100% rename from src/pgxc/pgxc_clean/gtm.c rename to src/pgxc/pgxc_clean/pgxc_clean_gtm.c ----------------------------------------------------------------------- Summary of changes: src/pgxc/pgxc_clean/Makefile | 2 +- src/pgxc/pgxc_clean/{gtm.c => pgxc_clean_gtm.c} | 0 2 files changed, 1 insertions(+), 1 deletions(-) rename src/pgxc/pgxc_clean/{gtm.c => pgxc_clean_gtm.c} (100%) hooks/post-receive -- Postgres-XC |
From: Koichi S. <koi...@us...> - 2011-03-30 03:42:05
|
Project "Postgres-XC". The branch, ha_support has been updated via dcc82f72fb630f16c965e1e6d3d93b2c6008a150 (commit) from 5dbdc5f9e7460a0f1f06cbbba96303a8bf7465fb (commit) - Log ----------------------------------------------------------------- commit dcc82f72fb630f16c965e1e6d3d93b2c6008a150 Author: Koichi Suzuki <koi...@gm...> Date: Wed Mar 30 12:41:37 2011 +0900 This is an additional commit for pgxc_clean to modifie it's parent Makefile to include pgxc_clean make. diff --git a/src/pgxc/Makefile b/src/pgxc/Makefile index 5ba9ac1..2156013 100644 --- a/src/pgxc/Makefile +++ b/src/pgxc/Makefile @@ -13,7 +13,7 @@ subdir = src/pgxc top_builddir = ../.. include $(top_builddir)/src/Makefile.global -DIRS = xcm +DIRS = xcm pgxc_clean all install installdirs uninstall distprep clean distclean maintainer-clean: $(INSTALL_DATA) $(srcdir)/xcm/pgxc_ha.conf.sample '$(DESTDIR)$(datadir)/pgxc_ha.conf.sample' ----------------------------------------------------------------------- Summary of changes: src/pgxc/Makefile | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) hooks/post-receive -- Postgres-XC |
From: Koichi S. <koi...@us...> - 2011-03-30 03:08:40
|
Project "Postgres-XC". The branch, ha_support has been updated via 5dbdc5f9e7460a0f1f06cbbba96303a8bf7465fb (commit) from 4d3f0a48a979044c6b741f9cd7d4dc37c32548e2 (commit) - Log ----------------------------------------------------------------- commit 5dbdc5f9e7460a0f1f06cbbba96303a8bf7465fb Author: Koichi Suzuki <koi...@gm...> Date: Wed Mar 30 12:02:46 2011 +0900 This commit is to add "pgxc_clean" tools for HA-support. When a coordinator fails, transactions handled in the coordinator may fail. HOwever, the other coordinator can continue to handle incoming transactions. On the other hand, if there's prepared but not committed/aborted transactions originated from the failed coordinator, it will reman as "unfinished transactions" whose GXID will continue to appear in global snapshots, which gives bad influence to the performance. pgxc_clean deals with this situation. It looks for 2PC transactions and if it is originated by the failed coordinator and if they are not finished. Then pgxc_config will commit or abort (depending upon the whole status of local commits) such transactions so that they don't appear in snapshots. This is implemented by Metro Systems. diff --git a/src/pgxc/pgxc_clean/Makefile b/src/pgxc/pgxc_clean/Makefile new file mode 100644 index 0000000..10cba88 --- /dev/null +++ b/src/pgxc/pgxc_clean/Makefile @@ -0,0 +1,51 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/pgxc/pgxc_clean +# +# Portions Copyright (c) 2011 Nippon Telegraph and Telephone Corporation +# +# $PostgreSQL$ +# +#------------------------------------------------------------------------- + +PGFILEDESC = "pgxc_clean - Abort prepared transaction for a Postgres-XC Coordinator" +subdir = src/pgxc/pgxc_clean +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) + +xcm_builddir = $(top_builddir)/src/pgxc/xcm +gtm_builddir = $(top_builddir)/src/gtm + +OBJS= pgxc_clean.o common.o subfunc.o txnctl.o gtm.o +# Switch when libxcm and libgtmclient are installed via 'make install' +# EX_OBJS = $(gtm_builddir)/common/assert.o +EX_OBJS = $(gtm_builddir)/common/assert.o \ + $(gtm_builddir)/client/libgtmclient.a \ + $(xcm_builddir)/libxcm.a + +override LDFLAGS += -L$(top_builddir)/src/gtm/client \ + -L$(top_builddir)/src/pgxc/xcm + +# Switch when libxcm and libgtmclient are installed via 'make install' +# LIBS= -lpthread -lxcm -lgtmclient +LIBS= -lpthread + +all: pgxc_clean + +pgxc_clean: $(OBJS) + $(CC) $(CFLAGS) $(OBJS) $(EX_OBJS) $(libpq_pgport) $(LDFLAGS) $(LIBS) -o $@$(X) + +install: all installdirs + $(INSTALL_PROGRAM) pgxc_clean$(X) '$(DESTDIR)$(bindir)'/pgxc_clean$(X) + +installdirs: + $(mkinstalldirs) '$(DESTDIR)$(bindir)' + +uninstall: + rm -f $(addprefix '$(DESTDIR)$(bindir)'/, pgxc_clean$(X)) + +clean distclean maintainer-clean: + rm -f pgxc_clean$(X) $(OBJS) pgxc_clean.o + diff --git a/src/pgxc/pgxc_clean/README b/src/pgxc/pgxc_clean/README new file mode 100644 index 0000000..a93d617 --- /dev/null +++ b/src/pgxc/pgxc_clean/README @@ -0,0 +1,55 @@ + +Notes on pgxc_clean +================ + +1. Abstract: +------------ + +pgxc_clean is a command line tool which recover 2PC-transactions which are +originated by crashed coordinator. + +2. Usage: +--------- + +Usage: + pgxc_clean --version + pgxc_clean --help + pgxc_clean [--all|--node=NODE_NUMBER] [--user=USERNAME] [--password=PASSWORD] + +General options: + --help show help, then exit + --version output version information, then exit + +Options for originator: + -a, --all all Coordinators in the cluster + -n, --node=NODE_NUMBER given Coordinator number + +Connection options: + -U, --user=USERNAME username to be used to establish connection + against coordinator + -W, --password=PASSWORD password to be used to establish connection + against coordinator + +3. Logging: +----------- + +pgxc_clean uses stdout to output non-error messages, and uses stderr to output +error or warning messages. + +4. Exit status: +--------------- + +pgxc_clean uses different exit codes for each type of error. + + 0: cleanup has been finished successfully + 4: wrong arguement was specified in command line + 6: pgxc_clean tried to connect to all running coordinators, but none of + them accepts connection request + 8: some kind of problem has occurred on GTM component + 9: some kind of problem has occurred on xcm component + 10: some kind of problem has occurred on coordinator component + 11: some kind of problem has occurred on datanode component + 12: pgxc_clean found transaction which has invalid status, such as + committed on a node but aborted on another node + 16: pgxc_clean encountered internal error, mostly out-of-memory + diff --git a/src/pgxc/pgxc_clean/common.c b/src/pgxc/pgxc_clean/common.c new file mode 100644 index 0000000..3f01f67 --- /dev/null +++ b/src/pgxc/pgxc_clean/common.c @@ -0,0 +1,81 @@ +/*------------------------------------------------------------------------- + * + * common.c + * common functions for pgxc_clean + * + * Portions Copyright (c) 2011 Nippon Telegraph and Telephone Corporation + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + +#include "pgxc_clean.h" + +extern const char *progname; + +/* + * output messages to console + * to stderr: warning and error message + * to stdout: others + */ +void +dispmsg(errlevel el, const char *format, ...) +{ + va_list vl; + time_t t; + struct tm *now; + const char *lvlstr[] = {"[?] ", "[I] ", "[W] ", "[E] ", "[D] "}; + + /* ignore debug messages in non-debug build */ +#ifndef DEBUG + if (el == lvl_debug) + return; +#endif + + time(&t); + now = localtime(&t); + + + if (el == lvl_warn || el == lvl_error) + fprintf(stderr, "[%s] %04d/%02d/%02d %02d:%02d:%02d ", + progname, now->tm_year+1900, now->tm_mon+1, now->tm_mday, + now->tm_hour, now->tm_min, now->tm_sec); + else + fprintf(stdout, "[%s] %04d/%02d/%02d %02d:%02d:%02d ", + progname, now->tm_year+1900, now->tm_mon+1, now->tm_mday, + now->tm_hour, now->tm_min, now->tm_sec); + + switch (el) + { + case lvl_info: + fprintf(stdout, lvlstr[el]); + break; + case lvl_warn: + case lvl_error: + fprintf(stderr, lvlstr[el]); + break; + case lvl_debug: + fprintf(stdout, lvlstr[el]); + break; + default: + fprintf(stdout, lvlstr[0]); + break; + } + + va_start(vl, format); + if (el == lvl_warn || el == lvl_error) + vfprintf(stderr, format, vl); + else + vfprintf(stdout, format, vl); + va_end(vl); + + if (el == lvl_warn || el == lvl_error) + fprintf(stderr, "\n"); + else + { + fprintf(stdout, "\n"); + fflush(stdout); + } +} diff --git a/src/pgxc/pgxc_clean/gtm.c b/src/pgxc/pgxc_clean/gtm.c new file mode 100644 index 0000000..e188ba2 --- /dev/null +++ b/src/pgxc/pgxc_clean/gtm.c @@ -0,0 +1,139 @@ +/*------------------------------------------------------------------------- + * + * gtm.c + * GTM accessing functions + * + * Portions Copyright (c) 2011 Nippon Telegraph and Telephone Corporation + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/xact.h" +#include "gtm/gtm_client.h" +#include "gtm/libpq-int.h" +#include "pgxc/xcm/node_membership.h" + +#include "pgxc_clean.h" + +static GTM_Conn *conn_gtm; /* connection to the GTM */ + +/* + * connect2gtm + * connect to GTM, not GTM-proxy + */ +void +connect2gtm(void) +{ + int nRet; + xcm_connPoint *conn_pts; + int n_connections; + char connstr[256]; + + /* XXX should use gtm_proxy? */ + nRet = get_xcm_gtm_connPoints(1, &conn_pts, &n_connections); + if (nRet != XCM_OK) + { + dispmsg(lvl_error, "couldn't get connection point for GTM"); + exit(RESULT_ERR_XCM); + } + + /* build connection string, we don't check overflow */ + snprintf(connstr, sizeof(connstr), + "host=%s port=%d pgxc_node_id=%d remote_type=%d", + conn_pts->addr, conn_pts->port, 0, PGXC_NODE_COORDINATOR); + dispmsg(lvl_debug, "connstr for GTM is \"%s\"", connstr); + free_xcm_connPoints(conn_pts, n_connections); + + /* connect to GTM */ + conn_gtm = connect_gtm(connstr); + if (GTMPQstatus(conn_gtm) != CONNECTION_OK) + { + dispmsg(lvl_error, "couldn't connect to GTM: %s", + GTMPQerrorMessage(conn_gtm)); + exit(RESULT_ERR_GTM); + } + + dispmsg(lvl_debug, "connected to GTM"); +} + +/* + * disconnect2gtm + * disconnect from GTM + */ +void +disconnect2gtm(void) +{ + disconnect_gtm(conn_gtm); +} + +/* + * remove_gxid_from_snapshot + * remove gid of transaction which has been cleaned-up from GTM's snapshot + */ +void +remove_gxid_from_snapshot(GlobalTransactionId gxid, bool iscommit) +{ + dispmsg(lvl_debug, "BEGIN: remove_gxid_from_snapshot(gid=%d, %s)", + gxid, iscommit ? "commit" : "rollback"); + + if (GTMPQstatus(conn_gtm) != CONNECTION_OK) + { + dispmsg(lvl_error, "invalid connection for GTM: %s", + GTMPQerrorMessage(conn_gtm)); + exit(RESULT_ERR_INTERNAL); + } + + /* do cleanup */ + if (commit_transaction(conn_gtm, gxid) != 0) + { + dispmsg(lvl_error, "couldn't remove GXID from snapshot: %s", + GTMPQerrorMessage(conn_gtm)); + exit(RESULT_ERR_GTM); + } + dispmsg(lvl_debug, "gxid=%u removed from snapshot", gxid); +} + +/* + * dump_snapshot + * dump current snapshot in the GTM node + */ +#ifdef DEBUG +void +dump_snapshot(void) +{ + GTM_SnapshotData *snapshot; + GlobalTransactionId gxid; + uint32 snidx; + + dispmsg(lvl_debug, "== %s() ======================", __FUNCTION__); + + gxid = begin_transaction(conn_gtm, GTM_ISOLATION_RC, NULL); + if (gxid == InvalidGlobalTransactionId) + { + dispmsg(lvl_error, "BEGIN transaction failed: %s", + GTMPQerrorMessage(conn_gtm)); + return; + } + else + dispmsg(lvl_debug, "Started a new transaction (GXID:%u)", gxid); + + snapshot = get_snapshot(conn_gtm, gxid, true); + if (snapshot == NULL) + dispmsg(lvl_error, "couldn't get snapshot: %s", + GTMPQerrorMessage(conn_gtm)); + else + for (snidx = 0; snidx < snapshot->sn_xcnt; snidx++) + dispmsg(lvl_debug, "sn_xip[%u]=%d", snidx, snapshot->sn_xip[snidx]); + + if (commit_transaction(conn_gtm, gxid)) + dispmsg(lvl_debug, "COMMIT failed (GXID:%u): %s", gxid, + GTMPQerrorMessage(conn_gtm)); + + dispmsg(lvl_debug, "== %s() ======================", __FUNCTION__); +} +#endif diff --git a/src/pgxc/pgxc_clean/pgxc_clean.c b/src/pgxc/pgxc_clean/pgxc_clean.c new file mode 100644 index 0000000..e81230d --- /dev/null +++ b/src/pgxc/pgxc_clean/pgxc_clean.c @@ -0,0 +1,337 @@ +/*------------------------------------------------------------------------- + * + * pgxc_clean.c + * pgxc_clean is a recovery tool for the coordinator crash. + * + * Portions Copyright (c) 2011 Nippon Telegraph and Telephone Corporation + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + +#include "getopt_long.h" + +#include "pgxc_clean.h" +#include "txnctl.h" + +extern char *optarg; +extern int optind, + opterr; + +/* Global */ +const char *progname; + +/* number of nodes */ +int all_coordinator_cnt; /* # of coordinators in the cluster */ +int all_datanode_cnt; /* # of datanodes in the cluster */ +int all_mirror_cnt; /* # of datanode mirrors in the cluster */ + +/* status of each node */ +unsigned *coordsts; /* status of each coordinator */ +MirrorStatus *mirrorsts; /* status of each datanode mirrors */ + +/* result summary */ +uint32 committed; /* number of committed txns */ +uint32 aborted; /* number of aborted txns */ +uint32 didnothing; /* number of do-nothing txns */ + +/* for mirror_mode */ +bool is_mirror_mode_on; /* true means mirror_mode=on */ + +/* static functions */ +static void checkarg(int argc, + char *argv[], + PGXC_NodeId *org_coordid, + char **host, + char **port); +static void help(const char *progname); + +/*------------------------------------------------------------------------- + * pgxc_clean + * + * option parameters: + * --all:recovery all coordinators + * --node:recovery coordinator number + * --username:USERNAME for connect to coordinator + * --password:PASSWORD for connect to coordinator + * + * exit code: + * 0: cleanup has been finished successfully + * 4: wrong arguement was specified in command line + * 6: pgxc_clean tried to connect to all running coordinators, but none + * of them accepts connection request + * 8: some kind of problem has occurred on GTM component + * 9: some kind of problem has occurred on xcm component + * 10: some kind of problem has occurred on coordinator component + * 11: some kind of problem has occurred on datanode component + * 12: pgxc_clean found transaction which has invalid status, such as + * committed on a node but aborted on another node + * 16: pgxc_clean encountered internal error, mostly out-of-memory + * + * notes + * - assume that node configuration is never changed + * -> If a node is added after invoking pgxc_clean, it will be ignored. + * -> If a node fails after invoking pgxc_clean, the failure will be + * detected by pgxc_clean and reported through exit code and log. + * - don't care lock about shared memory for xcm module, just use via API + *------------------------------------------------------------------------- + */ +int +main(int argc, char *argv[]) +{ + char *user = NULL; /* given username */ + char *passwd = NULL; /* given password */ + PGXC_NodeId org_coordid; /* given org coordid, 0 means all */ + PGXC_NodeId coordid; /* temporary coordid */ + + set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgxc_clean")); + + /* initialize global variables */ + progname = get_progname(argv[0]); + all_coordinator_cnt = 0; + all_datanode_cnt = 0; + committed = 0; + aborted = 0; + didnothing = 0; + is_mirror_mode_on = false; + + /* check arguments */ + checkarg(argc, argv, &org_coordid, &user, &passwd); + dispmsg(lvl_debug, "%s start object coord=%u user=%s passwd=%s", + progname, org_coordid, user, passwd); + + /* get number of nodes, and status of each node */ + get_cluster_status(); + + /* originator node id must not exceed all_coordinator_cnt */ + if (org_coordid > all_coordinator_cnt) + { + fprintf(stderr, _("%s: NODE_NUMBER %u is out of range\n"), progname, + org_coordid); + exit(RESULT_ERR_ARGV); + } + + /* connect to a coordinator to execute queries */ + connect2coord(user, passwd); + + /* get mirror_mode in this cluster */ + get_mirror_mode(); + + /* connect to GTM */ + connect2gtm(); + + /* dump current snapshot for debug use */ + dump_snapshot(); + + /* + * Get list of 2PC-transactions which were originated on each coordinator, + * and clean them up. + */ + for (coordid = 1; coordid <= all_coordinator_cnt; coordid++) + { + int curr_txidx; /* current txn's index */ + + dispmsg(lvl_debug, "######## doing for originator %u.", coordid); + + /* Skip if originator was specified and this is not it. */ + if (org_coordid != 0 && org_coordid != coordid) + continue; + + /* initialize txinfo area */ + clear_txinfo(); + + /* Get list of transactions which were originated on the coordinator. */ + get_prepared_transactions(coordid); + + for (curr_txidx = 0; curr_txidx < txcnt; curr_txidx++) + { + int result; + + /* Get status of the transaction on each related node. */ + get_committed_transactions(curr_txidx); + + /* Cleanup the transaction */ + result = do_cleanup(curr_txidx); + switch (result) + { + case CLEANUP_COMMIT: + committed++; + break; + case CLEANUP_ROLLBACK: + aborted++; + break; + case CLEANUP_NOTHING: + didnothing++; + break; + case CLEANUP_SKIP: + /* We never reach here */ + break; + default: + break; + } + + } + } + + /* dump current snapshot for debug use */ + dump_snapshot(); + + /* Disconnect from coordinator */ + disconnect2coord(); + + /* Disconnect from GTM */ + disconnect2gtm(); + + /* Print summary of cleanup results. */ + dispmsg(lvl_info, + "cleanup finished: committed=%d aborted=%d did-nothing=%d", + committed, aborted, didnothing); + + exit(RESULT_SUCCESS); +} + + +/*------------------------------------------------------------------------- + * checkarg + * parse command line arguments and return settings through parameters. + * + * returns: + * N/A + *------------------------------------------------------------------------- + */ +static void +checkarg( + int argc, + char *argv[], + PGXC_NodeId *org_coordid, + char **user, + char **passwd) +{ + int c; + int optindex; + bool all_coordinators = false; /* --all/-a was specified */ + bool node_number = false; /* --node/-n was specified */ + static struct option long_options[] = { + {"all", no_argument, NULL, 'a'}, + {"node", required_argument, NULL, 'n'}, + {"username", required_argument, NULL, 'U'}, + {"password", required_argument, NULL, 'W'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, '?'}, + {NULL, 0, NULL, 0} + }; + + while ((c = getopt_long(argc, argv, "an:U:W:V?", + long_options, &optindex)) != -1) + { + switch (c) + { + case 'a': + all_coordinators = true; + break; + case 'n': + /* check option parameters */ + /* -n NODE_NUMBER */ + /* NODE_NUMBER needs greater than 1 */ + node_number = true; + if (atoi(optarg) <= 0) + { + fprintf(stderr, _("%s: NODE_NUMBER needs greater than 0.\n"), + progname); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), + progname); + exit(RESULT_ERR_ARGV); + } + *org_coordid = atoi(optarg); + break; + case 'U': + *user = optarg; + break; + case 'W': + *passwd = optarg; + break; + case 'V': + puts("pgxc_clean (PostgreSQL) " PG_VERSION); + exit(RESULT_SUCCESS); + break; + case '?': + if (strcmp(argv[optind - 1], "-?") == 0 || + strcmp(argv[optind - 1], "--help") == 0) + { + help(progname); + exit(RESULT_SUCCESS); + } + else + { + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); + exit(RESULT_ERR_ARGV); + } + break; + default: + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); + exit(RESULT_ERR_ARGV); + break; + } + } + + /* receive user and password for libpq connection */ + if (argc > optind) + *user = strdup(argv[optind]); + if (argc > optind + 1) + *passwd = strdup(argv[optind + 1]); + if (argc > optind + 2) + { + fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), + progname, argv[optind + 2]); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), + progname); + exit(RESULT_ERR_ARGV); + } + + /* --all and --node are exclusive */ + if (all_coordinators && node_number) + { + fprintf(stderr, _("%s: --all and --node can't be specified together.\n"), + progname); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), + progname); + exit(RESULT_ERR_ARGV); + } + + /* + * We treat all transactions which were originated by all coordinators if + * --all is specified, or neither of --all nor --node was specified. + */ + if (all_coordinators || !node_number) + *org_coordid = 0; + + return; + +} + +/*------------------------------------------------------------------------- + * show help messages + *------------------------------------------------------------------------- + */ +static void +help(const char *progname) +{ + printf(_("%s is a utility to clean up 2PC-transactions.\n\n"), progname); + printf(_("Usage:\n")); + printf(_(" %s [OPTION]\n"), progname); + + printf(_("\nGeneral options:\n")); + printf(_(" -?, --help show this help, then exit\n")); + printf(_(" -V, --version output version information, then exit\n")); + + printf(_("\nOptions for originator:\n")); + printf(_(" -a, --all Clean up all transactions\n")); + printf(_(" -n, --node=NODE_NUMBER Clean up transactions originated on the Coordinator\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -U, --username=USERNAME username used to connect to coordinator\n")); + printf(_(" -W, --password=PASSWORD password used to connect to coordinator\n")); + +} diff --git a/src/pgxc/pgxc_clean/pgxc_clean.h b/src/pgxc/pgxc_clean/pgxc_clean.h new file mode 100644 index 0000000..ae39eed --- /dev/null +++ b/src/pgxc/pgxc_clean/pgxc_clean.h @@ -0,0 +1,86 @@ +/*------------------------------------------------------------------------- + * + * pgxc_clean.h + * header file for the pgxc_clean + * + * Portions Copyright (c) 2011 Nippon Telegraph and Telephone Corporation + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + +#ifndef PGXC_CLEAN_H +#define PGXC_CLEAN_H + +#include "gtm/gtm_c.h" + +/* define program exit code */ +#define RESULT_SUCCESS 0 /* normal terminate */ +#define RESULT_ERR_ARGV 4 /* illegal format on option parameters */ +#define RESULT_ERR_CONN2COORD 6 /* couldn't connect to any coordinator */ +#define RESULT_ERR_GTM 8 /* failed to cooperate with GTM */ +#define RESULT_ERR_XCM 9 /* failed to cooperate with xcm */ +#define RESULT_ERR_COORD 10 /* failed to cooperate with coordinator */ +#define RESULT_ERR_DNODE 11 /* failed to cooperate with datanode */ +#define RESULT_ERR_TX_STATUS 12 /* transaction in invalid status found */ +#define RESULT_ERR_INTERNAL 16 /* pgxc_clean internal failure */ + +/* results of cleanup */ +#define CLEANUP_COMMIT 0 +#define CLEANUP_ROLLBACK 1 +#define CLEANUP_SKIP 2 +#define CLEANUP_NOTHING 3 + +/* define error level */ +typedef enum { + lvl_none = 0, + lvl_info, + lvl_warn, + lvl_error, + lvl_debug +} errlevel; + +/* + * Status of a datanode's mirror. + */ +typedef struct MirrorStatus { + PGXC_NodeId datanode_id; /* datanode id */ + int mirror_id; /* mirror id in the datanode */ + unsigned status; /* status of the mirror */ +} MirrorStatus; + +/* + * Status of each node. + * + * coordsts is allocated for all coordinators even if some of them failed. + * mirrorsts is allocated for all datanode mirrors even if some of them failed. + */ +extern unsigned *coordsts; +extern MirrorStatus *mirrorsts; + +/* prototypes */ +/* common.c */ +extern void dispmsg(errlevel el, const char *format, ...); + +/* subfunc.c */ +extern void get_mirror_mode(void); +extern void get_cluster_status(void); +extern void connect2coord(const char *user, const char *passwd); +extern void disconnect2coord(void); +extern void get_prepared_transactions(PGXC_NodeId org_coordid); +extern void get_committed_transactions(int txidx); +extern int do_cleanup(int txidx); +extern void clear_txinfo(void); + +/* gtm.c */ +void connect2gtm(void); +void remove_gxid_from_snapshot(GlobalTransactionId gxid, bool iscommit); +void disconnect2gtm(void); +#ifdef DEBUG +void dump_snapshot(void); +#else +#define dump_snapshot() +#endif + +#endif /* PGXC_CLEAN_H */ diff --git a/src/pgxc/pgxc_clean/subfunc.c b/src/pgxc/pgxc_clean/subfunc.c new file mode 100644 index 0000000..a194e04 --- /dev/null +++ b/src/pgxc/pgxc_clean/subfunc.c @@ -0,0 +1,986 @@ +/*------------------------------------------------------------------------- + * + * subfunc.c + * sub functions for pgxc_clean + * + * Portions Copyright (c) 2011 Nippon Telegraph and Telephone Corporation + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + +#include "gtm/gtm.h" +#include "gtm/gtm_txn.h" +#include "pgxc/xcm/node_membership.h" + +#include "pgxc_clean.h" +#include "txnctl.h" +#include "libpq-fe.h" + +/* + * SQL statement formats + */ + +/* SQL format for connected coordinator */ +#define SQLFMT_GETPREPAREDXACT "select * from pg_prepared_xact() where coordnum=%d;" +#define SQLFMT_ISCOMMITTED "select pgxc_is_committed('%u');" +#define SQLFMT_COMMIT "commit prepared '%s';" +#define SQLFMT_ROLLBACK "rollback prepared '%s';" + +/* EXECUTE DIRECT SQL format for un-connected coordinators */ +#define SQLFMT_EDC_GETPREPAREDXACT "EXECUTE DIRECT ON COORDINATOR %d 'select * from pg_prepared_xact() where coordnum=%d';" +#define SQLFMT_EDC_ISCOMMITTED "EXECUTE DIRECT ON COORDINATOR %d 'select pgxc_is_committed(''%u'')';" +#define SQLFMT_EDC_COMMIT "EXECUTE DIRECT ON COORDINATOR %d 'commit prepared ''%s''';" +#define SQLFMT_EDC_ROLLBACK "EXECUTE DIRECT ON COORDINATOR %d 'rollback prepared ''%s''';" + +/* EXECUTE DIRECT SQL format for datanodes */ +#define SQLFMT_EDD_GETPREPAREDXACT "EXECUTE DIRECT ON NODE %s 'select * from pg_prepared_xact() where coordnum=%d';" +#define SQLFMT_EDD_ISCOMMITTED "EXECUTE DIRECT ON NODE %s 'select pgxc_is_committed(''%u'')';" +#define SQLFMT_EDD_COMMIT "EXECUTE DIRECT ON NODE %s 'commit prepared ''%s''';" +#define SQLFMT_EDD_ROLLBACK "EXECUTE DIRECT ON NODE %s 'rollback prepared ''%s''';" + +/* Get mirror_mode from connected coordinator */ +#define SQLFMT_SHOWMIRRORMODE "show mirror_mode;" + +/* + * EXECUTE DIRECT can't invoked COMMIT/ABORT PREPARED on the coordinator + * which pgxc_clean connected directly. So we have to use two connections, + * one is established without "remotetype" option for EXECUTE DIRECT on nodes + * other than connected coordinator, and another is established with + * "remotetype=coordinator" option for invoke transaction management commands + * on the connected coordinator. + */ +PGXC_NodeId con_coordid; /* id of connected coordinator */ +PGconn *conn_coord; /* connection for the coordinator */ +PGconn *conn_other; /* connection for other nodes */ + +extern bool is_mirror_mode_on; /* true means mirror_mode=on */ + +extern int all_coordinator_cnt; /* number of coords in cluster */ +extern int all_datanode_cnt; /* number of dnodes in cluster */ +extern int all_mirror_cnt; /* number of datanode mirrors in cluster */ + +/* static functions */ +static void get_nodests(void); +static bool contains_tx_sts(prepared_txninfo *txinfo, NODE_STS sts); +static void cleanup_txn(prepared_txninfo *txinfo, bool iscommit); + +/* + * get_mirror_mode + * get cluster's mirror_mode. + */ +void +get_mirror_mode(void) +{ + PGresult *res; + dispmsg(lvl_debug, "BEGIN: %s()", __FUNCTION__); + + /* + * Execute query to get mirror_mode. + */ + res = PQexec(conn_coord, SQLFMT_SHOWMIRRORMODE); + if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) + { + char *emsg = PQerrorMessage(conn_coord); + dispmsg(lvl_error, + "couldn't get mirror_mode: %s", emsg); + PQclear(res); + exit(RESULT_ERR_COORD); + } + + is_mirror_mode_on = (strcmp(PQgetvalue(res, 0, 0), "on") == 0); + PQclear(res); + + dispmsg(lvl_debug, "mirror_mode=%s", is_mirror_mode_on ? "on" : "off"); +} + +/* + * get_cluster_status + * get cluster configuration and status of each node in the cluster. + */ +void +get_cluster_status(void) +{ + int nRet; + + dispmsg(lvl_debug, "BEGIN: get_cluster_status()"); + + /* get number of coordinators from xcm */ + nRet = get_xcm_coordinator_count(&all_coordinator_cnt); + if (nRet != XCM_OK) + { + dispmsg(lvl_error, + "couldn't get number of coordinators in the cluster.(%d)", nRet); + exit(RESULT_ERR_XCM); + } + dispmsg(lvl_debug, "number of coordinators is %d", all_coordinator_cnt); + + /* get number of datanodes from xcm */ + nRet = get_xcm_datanode_count(&all_datanode_cnt); + if (nRet != XCM_OK) + { + dispmsg(lvl_error, + "couldn't get number of datanodes in the cluster.(%d)", nRet); + exit(RESULT_ERR_XCM); + } + dispmsg(lvl_debug, "number of datanodes is %d", all_datanode_cnt); + + /* get running status of all coordinators and datanodes */ + get_nodests(); +} + + +/*------------------------------------------------------------------------- + * connect2coord + * connect to a running coordinator in the cluster which has minimum id. + * + * returns: + * node id of connected coordinator, or 0 if all coordinator have failed. + *------------------------------------------------------------------------- + */ +void +connect2coord(const char *user, const char *passwd) +{ + int nRet; + int nconns = 0; + xcm_connPoint *xcwconn; + char port[10]; + int cdidx; + + dispmsg(lvl_debug, "BEGIN: connect2coord(%s, %s)", user, passwd); + + /* Try all coordinator in order of id */ + for (cdidx = 0; cdidx < all_coordinator_cnt; cdidx++) + { + dispmsg(lvl_debug, + "trying to connect to coordinator %d", + cdidx + 1); + + /* Ignore failed coordinators */ + if (XCM_IS_FAULT(coordsts[cdidx])) + continue; + + /* Get connection information from xcm module. */ + /* XXX: Should we use connection points other than first one? */ + nRet = get_xcm_coordinator_connPoints(cdidx + 1, &nconns, &xcwconn); + if (nRet != XCM_OK) + { + dispmsg(lvl_error, + "couldn't get connection point for coordinator %u", + cdidx + 1); + exit(RESULT_ERR_XCM); + } + + /* Convert port number to string */ + snprintf(port, sizeof(port), "%d", xcwconn->port); + + /* + * Connect to coordinator with remotetype option. This connection is + * used to execute ordinary query statements on the connected + * coordinator directly. + */ + conn_coord = PQsetdbLogin(xcwconn->addr, + port, + "-c remotetype=coordinator", + NULL, + "template1", + user, + passwd); + if (!conn_coord || PQstatus(conn_coord) != CONNECTION_OK) + { + dispmsg(lvl_warn, + "couldn't connect to coordinator %u with remotetype: %s", + cdidx + 1, + PQerrorMessage(conn_coord)); + PQfinish(conn_coord); + free_xcm_connPoints(xcwconn, nconns); + continue; + } + + /* + * Connect to coordinator with remotetype option. This connection is + * used to execute EXECUTE DIRECT statements on the node other than + * connected coordinator. + */ + conn_other = PQsetdbLogin(xcwconn->addr, + port, + NULL, + NULL, + "template1", + user, + passwd); + if (!conn_other || PQstatus(conn_other) != CONNECTION_OK) + { + dispmsg(lvl_warn, + "couldn't connect to coordinator %u without remotetype: %s", + cdidx + 1, + PQerrorMessage(conn_other)); + PQfinish(conn_coord); + PQfinish(conn_other); + free_xcm_connPoints(xcwconn, nconns); + continue; + } + + free_xcm_connPoints(xcwconn, nconns); + + /* Now we have two connections against a coordinator for cleanup. */ + con_coordid = cdidx + 1; + dispmsg(lvl_debug, "connected to coordinator %u", con_coordid); + + return; + } + + /* failed to establish connections against coordinator */ + dispmsg(lvl_error, "there is no running coordinator"); + exit(RESULT_ERR_CONN2COORD); +} + +void +disconnect2coord(void) +{ + PQfinish(conn_coord); + PQfinish(conn_other); +} + +/*------------------------------------------------------------------------- + * get_prepared_transactions + * Get list of 2PC-transactions which is prepared on any node. + *------------------------------------------------------------------------- + */ +void +get_prepared_transactions(PGXC_NodeId org_coordid) +{ + int nodeloop, txidx; + + dispmsg(lvl_debug, + "BEGIN: get_prepared_transactions(org_coordid=%u)", + org_coordid); + + /* + * Get list of prepared transactions from each coordinator with + * invoking pg_prepared_xact() and merge them into one list. + */ + for (nodeloop = 0; nodeloop < all_coordinator_cnt; nodeloop++) + { + PGXC_NodeId nodeid = (PGXC_NodeId) (nodeloop + 1); + PGresult *res; + char sql[512]; + bool on_connected_node = (nodeid == con_coordid); + PGconn *conn = on_connected_node ? conn_coord : conn_other; + + /* Ignore failed components */ + if (XCM_IS_FAULT(coordsts[nodeloop])) + continue; + + /* Construct query for pg_prepared_xact(). */ + if (on_connected_node) + snprintf(sql, sizeof(sql), SQLFMT_GETPREPAREDXACT, org_coordid); + else + snprintf(sql, sizeof(sql), SQLFMT_EDC_GETPREPAREDXACT, nodeid, + org_coordid); + + dispmsg(lvl_debug, "SQL=[%s]", sql); + + /* + * Execute query to get list of transactions which have been prepared + * on the node. + */ + res = PQexec(conn, sql); + if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) + { + char *emsg = PQerrorMessage(conn); + dispmsg(lvl_error, + "pg_prepared_xact() failed on coordinator %u for originator %u :%s", + nodeid, org_coordid, emsg); + PQclear(res); + exit(RESULT_ERR_COORD); + } + + /* store transaction status into the list */ + for (txidx = 0; txidx < PQntuples(res); txidx++) + { + TransactionId transaction; + bool isddl; + bool isimplicit; + + transaction = (TransactionId) atoi(PQgetvalue(res, txidx, 0)); + isddl = (strcmp(PQgetvalue(res, txidx, 5), "t") == 0); + isimplicit = (strcmp(PQgetvalue(res, txidx, 6), "t") == 0); + + set_txinfo( + org_coordid, /* originator coordinator id */ + true, /* True if source was coordinator */ + nodeid, /* nodeid */ + 0, /* mirror_id, 0 if iscoord */ + transaction, /* transaction */ + PQgetvalue(res, txidx, 1), /* gid */ + isddl, /* is_ddl */ + isimplicit, /* is_implicit */ + PQgetvalue(res, txidx, 8)); /* nodelist */ + + } + + PQclear(res); + } + + /* + * Get list of prepared transactions from each datanode with + * invoking pg_prepared_xact() and merge them into one list. + */ + for (nodeloop = 0; nodeloop < all_mirror_cnt; nodeloop++) + { + PGXC_NodeId nodeid = mirrorsts[nodeloop].datanode_id; + int mirror_id = mirrorsts[nodeloop].mirror_id; + PGresult *res; + char sql[512]; + char dnodestr[64]; + PGconn *conn = conn_other; + + /* Ignore failed components */ + if (XCM_IS_FAULT(mirrorsts[nodeloop].status)) + continue; + + /* Set datanode id */ + if (is_mirror_mode_on) + snprintf(dnodestr, sizeof(dnodestr), "%u/%d", nodeid, mirror_id); + else + snprintf(dnodestr, sizeof(dnodestr), "%u", nodeid); + + /* Construct EXECUTE DIRECT query for pg_prepared_xact(). */ + snprintf(sql, sizeof(sql), SQLFMT_EDD_GETPREPAREDXACT, + dnodestr, org_coordid); + + dispmsg(lvl_debug, "SQL=[%s]", sql); + + /* Execute query to get list of prepared transactions. */ + res = PQexec(conn, sql); + if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) + { + char *emsg = PQerrorMessage(conn); + dispmsg(lvl_error, + "pg_prepared_xact() failed on datanode %s for originator %u :%s", + dnodestr, org_coordid, emsg); + PQclear(res); + exit(RESULT_ERR_DNODE); + } + + /* store transaction status into the list */ + for (txidx = 0; txidx < PQntuples(res); txidx++) + { + TransactionId transaction; + bool isddl; + bool isimplicit; + + transaction = (TransactionId) atoi(PQgetvalue(res, txidx, 0)); + isddl = (strcmp(PQgetvalue(res, txidx, 5), "t") == 0); + isimplicit = (strcmp(PQgetvalue(res, txidx, 6), "t") == 0); + + set_txinfo( + org_coordid, /* originator coordinator id */ + false, /* true if source was coordinator */ + nodeid, /* nodeid */ + mirror_id, /* mirror_id, 0 if iscoord */ + transaction, /* transaction */ + PQgetvalue(res, txidx, 1), /* gid */ + isddl, /* is_ddl */ + isimplicit, /* is_implicit */ + PQgetvalue(res, txidx, 8)); /* nodelist */ + + } + + PQclear(res); + + } + + /* for debug use */ +#ifdef DEBUG + for(txidx = 0; txidx < txcnt; txidx++) + dump_txinfo(txidx); +#endif + + return; +} + +/*------------------------------------------------------------------------- + * get_committed_transactions + * update status of a transaction on a node to COMMITTED or ABORTED + * whose status has not been clarified. + *------------------------------------------------------------------------- + */ +void +get_committed_transactions(int txidx) +{ + int nodeidx; + prepared_txninfo *txinfo = NULL; + + dispmsg(lvl_debug, "BEGIN: get_committed_transactions(%d)", txidx); + + /* get prepared_txninfo */ + txinfo = get_prepared_txninfo(txidx); + if (!txinfo) + { + dispmsg(lvl_error, "couldn't get prepared transaction info."); + exit(RESULT_ERR_INTERNAL); + } + + /* coordinators */ + for (nodeidx = 0; nodeidx < all_coordinator_cnt; nodeidx++) + { + PGXC_NodeId nodeid = (PGXC_NodeId) (nodeidx + 1); + PGresult *res; + char sql[1024]; + bool on_connected_node = (nodeid == con_coordid); + PGconn *conn = on_connected_node ? conn_coord : conn_other; + + /* Ignore failed components */ + if (XCM_IS_FAULT(coordsts[nodeidx])) + continue; + + /* Ignore node whose status has been already known */ + if (txinfo->tx_sts_c[nodeidx] != STS_RELATED) + continue; + + /* Determine which connection should be used. */ + if (nodeid == con_coordid) + conn = conn_coord; + else + conn = conn_other; + + /* Construct EXECUTE DIRECT query for pgxc_is_committed(). */ + if (nodeid == con_coordid) + snprintf(sql, sizeof(sql), SQLFMT_ISCOMMITTED, txinfo->txid); + else + snprintf(sql, sizeof(sql), SQLFMT_EDC_ISCOMMITTED, + nodeid, txinfo->txid); + + dispmsg(lvl_debug, "SQL=[%s]", sql); + + /* Execute query to get is committed transaction. */ + res = PQexec(conn, sql); + if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) + { + char *emsg = PQerrorMessage(conn); + dispmsg(lvl_error, + "pgxc_is_committed() failed on coordinator %u for transaction %s: %s", + nodeid, txinfo->gid, emsg); + + PQclear(res); + exit(RESULT_ERR_COORD); + } + + /* update status */ + if (strcmp(PQgetvalue(res, 0, 0), "t") == 0) + set_tx_sts(txinfo, true, nodeid, 0, STS_COMMITTED); + else + set_tx_sts(txinfo, true, nodeid, 0, STS_ABORTED); + + PQclear(res); + } + + /* datanodes */ + for (nodeidx = 0; nodeidx < all_mirror_cnt; nodeidx++) + { + PGXC_NodeId nodeid = mirrorsts[nodeidx].datanode_id; + int mirror_id = mirrorsts[nodeidx].mirror_id; + PGresult *res; + char sql[512]; + char dnodestr[64]; + PGconn *conn = conn_other; + + /* Ignore failed components */ + if (XCM_IS_FAULT(mirrorsts[nodeidx].status)) + continue; + + if (txinfo->tx_sts_m[nodeidx] == STS_RELATED) + { + /* Set datanode id */ + if (is_mirror_mode_on) + snprintf(dnodestr, sizeof(dnodestr), "%u/%d", nodeid, mirror_id); + else + snprintf(dnodestr, sizeof(dnodestr), "%u", nodeid); + + /* Construct EXECUTE DIRECT query for pgxc_is_committed(). */ + snprintf(sql, sizeof(sql), SQLFMT_EDD_ISCOMMITTED, dnodestr, txinfo->txid); + dispmsg(lvl_debug, "SQL=[%s]", sql); + + /* Execute query to get is committed transaction. */ + res = PQexec(conn, sql); + if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) + { + char *emsg = PQerrorMessage(conn); + dispmsg(lvl_error, + "pgxc_is_committed() failed on datanode %s for transaction %s: %s", + dnodestr, txinfo->gid, emsg); + PQclear(res); + exit(RESULT_ERR_DNODE); + } + + /* update status */ + if (strcmp(PQgetvalue(res, 0, 0), "t") == 0) + set_tx_sts(txinfo, false, nodeid, mirror_id, STS_COMMITTED); + else + set_tx_sts(txinfo, false, nodeid, mirror_id, STS_ABORTED); + + PQclear(res); + } + } + + /* for debug use, removed on release build */ + dump_txinfo(txidx); +} + +/*------------------------------------------------------------------------- + * do_cleanup + * cleanup prepared transactions on each node along the status of the + * global transaction + *------------------------------------------------------------------------- + */ +int +do_cleanup(int txidx) +{ + prepared_txninfo *txinfo = NULL; + bool is_related, is_committed, is_aborted; + + dispmsg(lvl_debug, "BEGIN: do_cleanup(%d)", txidx); + + /* get prepared_txninfo */ + txinfo = get_prepared_txninfo(txidx); + if (!txinfo) + { + dispmsg(lvl_error, "couldn't get prepared transaction info."); + exit(RESULT_ERR_INTERNAL); + } + + /* get txn-status on each node */ + is_related = contains_tx_sts(txinfo, STS_RELATED); + is_committed = contains_tx_sts(txinfo, STS_COMMITTED); + is_aborted = contains_tx_sts(txinfo, STS_ABORTED); + + /* + * 1. RELATED node is contained(in-Prepare) + * + * 1-1. COMMITTED node is contained ==> illegal txn-condition + * 1-2. ABORTED node is contained ==> illegal txn-condition + * 1-3. otherwise ==> need rollback on prepared-node + */ + if (is_related) + { + /* + * If there are COMMITTED-node or ABORTED-node (or both), the transaction + * condition is wrong. + * Because transaction is not complete prepare-transaction. + */ + if(is_committed || is_aborted) + { + dispmsg(lvl_error, + "transaction(%d:%s) is bad condition. coord[%.*s] dnode[%.*s]", + txinfo->txid, txinfo->gid, all_coordinator_cnt, txinfo->tx_sts_c, + all_mirror_cnt, txinfo->tx_sts_m); + + exit(RESULT_ERR_TX_STATUS); + } + + /* + * The transaction is 'in-Prepare' condition. + * In this case, need to rollback transaction on PREPARED-node. + * XXX: We should do nothing if all node was prepared, shouldn't we? + */ + cleanup_txn(txinfo, false); + + /* Remove gid from GTM if the transaction was explicit-2PC */ + if (!txinfo->isimplicit) + remove_gxid_from_snapshot(txinfo->txid, false); + + return CLEANUP_ROLLBACK; + } + + /* + * A transaction which has critical contradiction was found, give + * up whole job. + */ + if (is_committed && is_aborted) + { + dispmsg(lvl_error, + "transaction(%d:%s) is bad condition. coord[%.*s] dnode[%.*s]", + txinfo->txid, txinfo->gid, all_coordinator_cnt, txinfo->tx_sts_c, + all_mirror_cnt, txinfo->tx_sts_m); + + exit(RESULT_ERR_TX_STATUS); + } + + /* + * 3. COMMITTED nodes only + * ==> need commit + */ + if (is_committed) + { + cleanup_txn(txinfo, true); + + /* Remove gid from GTM if the transaction was explicit-2PC */ + if (!txinfo->isimplicit) + remove_gxid_from_snapshot(txinfo->txid, true); + + return CLEANUP_COMMIT; + } + + /* + * 4. ABORTED nodes only + * ==> need rollback + */ + if (is_aborted) + { + cleanup_txn(txinfo, false); + + /* Remove gid from GTM if the transaction was explicit-2PC */ + if (!txinfo->isimplicit) + remove_gxid_from_snapshot(txinfo->txid, false); + + return CLEANUP_ROLLBACK; + } + + /* + * 5. PREPARED nodes only + * + * 5-1. implicit2PC transaction ==> need commit + * 5-2. explicit2PC transaction ==> do nothing + */ + if (!txinfo->isimplicit) + { + dispmsg(lvl_info, "do nothing."); + return CLEANUP_NOTHING; + } + + /* + * If no commit or abort are execurted, commit in all the involved + * nodes because it is obvious that the transaction was intended to + * be committed. + */ + cleanup_txn(txinfo, true); + + return CLEANUP_COMMIT; +} + +/*------------------------------------------------------------------------ + * clear_txinfo + * clear list of transaction and initialize it + *------------------------------------------------------------------------ + */ +void +clear_txinfo(void) +{ + dispmsg(lvl_debug, "BEGIN: clear_txinfo()"); + init_txinfo(); +} + +/*------------------------------------------------------------------------ + * static functions + *------------------------------------------------------------------------ + */ +/*------------------------------------------------------------------------ + * get_nodests + * get status of all nodes in the cluster + * + * Note: all_coordinator_cnt and all_datanode_cnt MUST be set before + * calling this function. + *------------------------------------------------------------------------ + */ +static void +get_nodests(void) +{ + int nodeloop; + int nRet; + MirrorStatus *mirror; + int failed_cnt; + + dispmsg(lvl_debug, "BEGIN: get_nodests()"); + + /* coordinators */ + /* malloc for sts area */ + coordsts = (unsigned *) malloc(sizeof(unsigned) * all_coordinator_cnt); + if (!coordsts) + { + dispmsg(lvl_error, "couldn't allocate memory for coordsts.(%d)", errno); + exit(RESULT_ERR_INTERNAL); + } + + /* get status of each coordinator from xcm */ + failed_cnt = 0; + for (nodeloop = 0; nodeloop < all_coordinator_cnt; nodeloop++) + { + nRet = get_xcm_coordinator_status(nodeloop + 1, &coordsts[nodeloop]); + if (nRet != XCM_OK && nRet != XCM_ERR_COMP_FAILED) + { + dispmsg(lvl_error, "couldn't get status of coord(%d).(%d)", + nodeloop + 1, nRet); + exit(RESULT_ERR_XCM); + } + failed_cnt += (nRet != XCM_OK); + dispmsg(lvl_debug, "status of coordinator %d is %d", nodeloop + 1, + coordsts[nodeloop]); + } + + /* + * If over half of coordinators have failed, it can be said that the whole + * cluster has failed, so we might give up to cleanup. + * Discussed on Feb 8, 2011. + */ + if (failed_cnt > all_coordinator_cnt / 2) + { + dispmsg(lvl_error, "too many coordinators failed (%d/%d)", + failed_cnt, all_coordinator_cnt); + exit(RESULT_ERR_COORD); + } + + /* count all datanode mirrors */ + all_mirror_cnt = 0; + for (nodeloop = 0; nodeloop < all_datanode_cnt; nodeloop++) + { + int mirror_cnt; + + nRet = get_xcm_mirror_count(nodeloop + 1, &mirror_cnt); + if (nRet != XCM_OK) + { + dispmsg(lvl_error, "couldn't get mirror count of datanode %d.(%d)", + nodeloop + 1, nRet); + exit(RESULT_ERR_XCM); + } + + all_mirror_cnt += mirror_cnt; + } + + /* allocate array of MirrorStatus for all of datanode mirrors */ + mirrorsts = (MirrorStatus *) malloc(sizeof(MirrorStatus) * all_mirror_cnt); + if (!mirrorsts) + { + dispmsg(lvl_error, "couldn't allocate memory for mirrorsts.(%d)", + errno); + exit(RESULT_ERR_INTERNAL); + } + + /* get status of each datanode mirror from xcm and store into mirrorsts */ + failed_cnt = 0; + mirror = mirrorsts; + for (nodeloop = 0; nodeloop < all_datanode_cnt; nodeloop++) + { + int mirrorcnt; + int mirrorloop; + char dnodestr[32]; + + nRet = get_xcm_mirror_count(nodeloop + 1, &mirrorcnt); + if (nRet != XCM_OK) + { + dispmsg(lvl_error, "couldn't get mirror count of datanode %d.(%d)", + nodeloop + 1, nRet); + exit(RESULT_ERR_XCM); + } + + for (mirrorloop = 0; mirrorloop < mirrorcnt; mirrorloop++) + { + mirror->datanode_id = nodeloop + 1; + mirror->mirror_id = mirrorloop + 1; + + /* Set datanode id */ + if (is_mirror_mode_on) + snprintf(dnodestr, sizeof(dnodestr), "%u/%d", mirror->datanode_id, mirror->mirror_id); + else + snprintf(dnodestr, sizeof(dnodestr), "%u", mirror->datanode_id); + + nRet = get_xcm_mirror_status(mirror->datanode_id, + mirror->mirror_id, + &mirror->status); + if (nRet != XCM_OK && nRet != XCM_ERR_COMP_FAILED) + { + dispmsg(lvl_error, "couldn't get status of datanode %s.(%d)", dnodestr, nRet); + free(coordsts); + free(mirrorsts); + exit(RESULT_ERR_XCM); + } + failed_cnt += (nRet != XCM_OK); + dispmsg(lvl_debug, "status of datanode %s is %d", dnodestr, mirror->status); + + mirror++; + } + } + + /* + * If over half of datanodes have failed, it can be said that the whole + * cluster has failed, so we might give up to cleanup. + * Discussed on Feb 8, 2011. + */ + if (failed_cnt > all_mirror_cnt / 2) + { + dispmsg(lvl_error, "too many datanodes failed (%d/%d)", + failed_cnt, all_mirror_cnt); + exit(RESULT_ERR_DNODE); + } + + return; +} + +/*------------------------------------------------------------------------- + * when transaction contains sts returns true + *------------------------------------------------------------------------- + */ +static bool +contains_tx_sts(prepared_txninfo *txinfo, NODE_STS sts) +{ + int nodeidx; + + if (!txinfo) + return false; + + /* coordinators */ + for (nodeidx = 0; nodeidx < all_coordinator_cnt; nodeidx++) + { + if (txinfo->tx_sts_c[nodeidx] == sts) + return true; + } + + /* datanodes */ + for (nodeidx = 0; nodeidx < all_mirror_cnt; nodeidx++) + { + if (txinfo->tx_sts_m[nodeidx] == sts) + return true; + } + + return false; +} + +/*------------------------------------------------------------------------- + * commit or rollback transaction on prepared node + *------------------------------------------------------------------------- + */ +static void +cleanup_txn(prepared_txninfo *txinfo, bool iscommit) +{ + int nodeidx; + char sql[512]; + PGresult *res; + uint32 coord_committed = 0; /* # of committed coordinators */ + uint32 coord_aborted = 0; /* # of aborted coordinators */ + uint32 dnode_committed = 0; /* # of committed datanodes */ + uint32 dnode_aborted = 0; /* # of aborted datanodes */ + + dispmsg(lvl_debug, "BEGIN: cleanup_txn(txid=%u, %s)", + txinfo ? txinfo->txid : 0, + iscommit ? "commit" : "rollback"); + + /* null pointer */ + if (!txinfo) + exit(RESULT_ERR_INTERNAL); + + /* coordinators */ + for (nodeidx = 0; nodeidx < all_coordinator_cnt; nodeidx++) + { + PGXC_NodeId nodeid = (PGXC_NodeId) (nodeidx + 1); + bool on_connected_node = (nodeid == con_coordid); + PGconn *conn = on_connected_node ? conn_coord : conn_other; + + /* Ignore failed components */ + if (XCM_IS_FAULT(coordsts[nodeidx])) + continue; + + if (txinfo->tx_sts_c[nodeidx] == STS_PREPARED) + { + /* Construct EXECUTE DIRECT for commit. */ + if (iscommit) + { + if (on_connected_node) + snprintf(sql, sizeof(sql), SQLFMT_COMMIT, txinfo->gid); + else + snprintf(sql, sizeof(sql), SQLFMT_EDC_COMMIT, + nodeid, txinfo->gid); + } + else + { + if (on_connected_node) + snprintf(sql, sizeof(sql), SQLFMT_ROLLBACK, txinfo->gid); + else + snprintf(sql, sizeof(sql), SQLFMT_EDC_ROLLBACK, + nodeid, txinfo->gid); + } + + dispmsg(lvl_debug, "SQL=[%s]", sql); + + /* Execute commit or rollback prepared transaction. */ + res = PQexec(conn, sql); + if (res == NULL || PQresultStatus(res) != PGRES_COMMAND_OK) + { + char *emsg = PQerrorMessage(conn); + dispmsg(lvl_error, + "couldn't %s on coordinator %u for cleanup transaction %s: %s", + iscommit ? "COMMIT" : "ABORT", nodeid, txinfo->gid, + emsg); + PQclear(res); + exit(RESULT_ERR_COORD); + } + + if (iscommit) + coord_committed++; + else + coord_aborted++; + + PQclear(res); + } + } + + /* datanodes */ + for (nodeidx = 0; nodeidx < all_mirror_cnt; nodeidx++) + { + PGXC_NodeId nodeid = mirrorsts[nodeidx].datanode_id; + int mirror_id = mirrorsts[nodeidx].mirror_id; + PGconn *conn = conn_other; + char dnodestr[64]; + + /* Ignore failed components */ + if (XCM_IS_FAULT(mirrorsts[nodeidx].status)) + continue; + + if (txinfo->tx_sts_m[nodeidx] == STS_PREPARED) + { + /* Set datanode id */ + if (is_mirror_mode_on) + snprintf(dnodestr, sizeof(dnodestr), "%u/%d", nodeid, mirror_id); + else + snprintf(dnodestr, sizeof(dnodestr), "%u", nodeid); + + /* Construct EXECUTE DIRECT for commit. */ + if (iscommit) + snprintf(sql, sizeof(sql), SQLFMT_EDD_COMMIT, + dnodestr, txinfo->gid); + else + snprintf(sql, sizeof(sql), SQLFMT_EDD_ROLLBACK, + dnodestr, txinfo->gid); + + dispmsg(lvl_debug, "SQL=[%s]", sql); + + /* Execute commit or rollback prepared transaction. */ + res = PQexec(conn, sql); + if (res == NULL || PQresultStatus(res) != PGRES_COMMAND_OK) + { + char *emsg = PQerrorMessage(conn); + dispmsg(lvl_error, + "couldn't %s on datanode %s for cleanup transaction %s: %s", + iscommit ? "COMMIT" : "ABORT", dnodestr, txinfo->gid, emsg); + PQclear(res); + exit(RESULT_ERR_DNODE); + } + + if (iscommit) + dnode_committed++; + else + dnode_aborted++; + + PQclear(res); + } + } + + if (iscommit) + dispmsg(lvl_info, "txid=%u coordinator=%u datanode=%u", + txinfo->txid, coord_committed, dnode_committed); + else + dispmsg(lvl_info, "txid=%u coordinator=%u datanode=%u", + txinfo->txid, coord_aborted, dnode_aborted); +} diff --git a/src/pgxc/pgxc_clean/txnctl.c b/src/pgxc/pgxc_clean/txnctl.c new file mode 100644 index 0000000..cc34a3c --- /dev/null +++ b/src/pgxc/pgxc_clean/txnctl.c @@ -0,0 +1,495 @@ +/*------------------------------------------------------------------------- + * + * txnctl.c + * control for transaction information + * + * Portions Copyright (c) 2011 Nippon Telegraph and Telephone Corporation + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + +#include <string.h> + +#include "gtm/gtm_c.h" +#include "access/gtm.h" +#include "pgxc/xcm/node_membership.h" + +#include "pgxc_clean.h" +#include "txnctl.h" + +/* Global variables */ +/* external */ +extern int prepared_txcnt; /* number of prepared transactions */ +extern int all_coordinator_cnt; /* number of coords in cluster */ +extern int all_datanode_cnt; /* number of dnodes in cluster */ +extern int all_mirror_cnt; /* number of mirrors in cluster */ +extern bool is_mirror_mode_on; /* true means mirror_mode=on */ +/* status of each node */ +extern unsigned *coordsts; /* status of each coordinator */ +extern MirrorStatus *mirrorsts; /* status of each datanode mirrors */ + +/* private */ +prepared_txninfo **txinfo; /* transaction information structure */ +int txcnt; /* number of registered transactions */ +int malloccnt; /* number of allocated memory */ + +/* private functions */ +static void set_mirrorsts(const char *nodelist, prepared_txninfo *tx, bool iscoord); +static void add_txinfo_area(void); + +/* + * Create new prepared_txninfo entry with given information. + */ +static prepared_txninfo * +create_prepared_txninfo( + PGXC_NodeId org_coordid, + bool iscoord, + PGXC_NodeId nodeid, + TransactionId txid, + char *gid, + bool isddl, + bool isimplicit, + char *nodelist) +{ + prepared_txninfo *mytx; + NODE_STS *coords = NULL; + NODE_STS *mirrors = NULL; + int nodeloop; + + /* + * New transaction was found, allocate memory for one transaction + * information. + */ + mytx = (prepared_txninfo *) calloc(1, sizeof(prepared_txninfo)); + if (!mytx) + { + dispmsg(lvl_error, + "couldn't allocate memory for transaction information.(%d)", + errno); + exit(RESULT_ERR_INTERNAL); + } + + /* Initialize the entry */ + mytx->org_coordid = org_coordid; + mytx->txid = txid; + mytx->gid = strdup(gid); + if (!mytx->gid) + { + dispmsg(lvl_error, "couldn't allocate memory for gid.(%d)", errno); + exit(RESULT_ERR_INTERNAL); + } + mytx->isddl = isddl; + mytx->isimplicit = isimplicit; + mytx->nodelist = strdup(nodelist); /* XXX: necessary? */ + if (!mytx->nodelist) + { + dispmsg(lvl_error, "couldn't allocate memory for nodelist.(%d)", + errno); + exit(RESULT_ERR_INTERNAL); + } + mytx->tx_sts_c = NULL; + mytx->tx_sts_m = NULL; + + /* + * Allocate status-array for all coordinators in the cluster, though we + * care coordinators only when a transaction includes DDL. + */ + coords = (NODE_STS *) malloc(sizeof(NODE_STS) * all_coordinator_cnt); + if (!coords) + { + dispmsg(lvl_error, + "couldn't allocate memory for coordnode sts.(%d)", errno); + exit(RESULT_ERR_INTERNAL); + } + for (nodeloop = 0; nodeloop < all_coordinator_cnt; nodeloop++) + { + if ( XCM_IS_FAULT(coordsts[nodeloop]) ) + coords[nodeloop] = STS_FAILED; + else + coords[nodeloop] = STS_UNKNOWN; + } + mytx->tx_sts_c = coords; + + /* + * Allocate status-array for all datanode mirrors in the cluster. + */ + mirrors = (NODE_STS *) malloc(sizeof(NODE_STS) * all_mirror_cnt); + if (!mirrors) + { + dispmsg(lvl_error, + "couldn't allocate memory for datanode sts.(%d)", errno); + exit(RESULT_ERR_INTERNAL); + } + for (nodeloop = 0; nodeloop < all_mirror_cnt; nodeloop++) + { + if (XCM_IS_FAULT(mirrorsts[nodeloop].status)) + mirrors[nodeloop] = STS_FAILED; + else + mirrors[nodeloop] = STS_UNKNOWN; + } + mytx->tx_sts_m = mirrors; + + return mytx; +} + +/*------------------------------------------------------------------------- + * set_txinfo + * store transaction information gotten from a node into transaction list. + * + * parameters: + * PGXC_NodeId org_coordid:çºè¡å coordid + * bool iscoord: True if the source is a coordinator + * PGXC_NodeId nodeid: id of source node + * TransactionId txid:pg_prepared_xact().transaction + * char *gid:pg_prepared_xact().gid + * bool isddl:pg_prepared_xact().is_ddl + * bool isimplicit:pg_prepared_xact().is_implicit + * char *nodelist:pg_prepared_xact().nodelist + * + * returns: + * N/A + *------------------------------------------------------------------------- + */ +void +set_txinfo( + PGXC_NodeId org_coordid, + bool iscoord, + PGXC_NodeId nodeid, + int mirror_id, + TransactionId txid, + char *gid, + bool isddl, + bool isimplicit, + char *nodelist) + +{ + prepared_txninfo *mytx = NULL; + + dispmsg(lvl_debug, "BEGIN: set_txinfo(%d, %s, %u, %d, %u, %s, %s, %s, %s)", + org_coordid, iscoord ? "TRUE" : "FALSE", nodeid, mirror_id, txid, + gid, isddl ? "TRUE" : "FALSE", isimplicit ? "TRUE" : "FALSE", nodelist); + + /* + * If this transaction has not been stored yet, create new transaction + * entry for it. + */ + mytx = get_tx(txid); + if (mytx == NULL) + { + /* Create entry for newly found transaction */ + mytx = create_prepared_txninfo(org_coordid, iscoord, nodeid, txid, gid, + isddl, isimplicit, nodelist); + + /* Expand transaction slot if necessary */ + if (txcnt == malloccnt) + add_txinfo_area(); + + /* store the transaction into transaction list */ + txinfo[txcnt++] = mytx; + } + + /* All coordinators are related node when isddl was true */ + if (mytx->isddl) + { + int coordidx; + + for (coordidx = 0; coordidx < all_coordinator_cnt; coordidx++) + if (mytx->tx_sts_c[coordidx] == STS_UNKNOWN) + mytx->tx_sts_c[coordidx] = STS_RELATED; + } + + /* set status of related datanode to RELATED */ + set_mirrorsts(nodelist, mytx, iscoord); + + /* + * The status of the transaction on the source node is PREPARED, because + * pg_prepared_xact() entry was found on the node. + */ + set_tx_sts(mytx, iscoord, nodeid, mirror_id, STS_PREPARED); + + return; +} + +/*------------------------------------------------------------------------- + * initialize transaction information + *------------------------------------------------------------------------- + */ +void +init_txinfo(void) +{ + dispmsg(lvl_debug, "BEGIN: init_txinfo()"); + + if (txinfo) + { + int txidx; + + for (txidx = 0; txidx < txcnt; txidx++) + { + free(txinfo[txidx]->gid); + free(txinfo[txidx]->nodelist); + free(txinfo[txidx]->tx_sts_c); + free(txinfo[txidx]->tx_sts_m); + free(txinfo[txidx]); + } + free(txinfo); + txinfo = NULL; + } + malloccnt = 0; + txcnt = 0; +} + +/*------------------------------------------------------------------------- + * dump transaction information + *------------------------------------------------------------------------- + */ +#ifdef DEBUG +void +dump_txinfo(int txidx) +{ + dispmsg(lvl_debug, "BEGIN: dump_txinfo(%d/%d)", txidx + 1, txcnt); + + printf("txinfo[%d]=%p\n", txidx, txinfo[txidx]); + + printf("[%d]org_coordid=%d(%p)\n", + txidx, txinfo[txidx]->org_coordid, &txinfo[txidx]->org_coordid); + printf("[%d]txid=%d(%p)\n", + txidx, txinfo[txidx]->txid, &txinfo[txidx]->txid); + printf("[%d]gid=[%s](%p)\n", + txidx, txinfo[txidx]->gid, txinfo[txidx]->gid); + printf("[%d]isddl=%s(%p)\n", + txidx, txinfo[txidx]->isddl ? "TRUE" : "FALSE", &txinfo[txidx]->isddl); + printf("[%d]isimplicit=%s(%p)\n", + txidx, txinfo[txidx]->isimplicit ? "TRUE" : "FALSE", &txinfo[txidx]->isimplicit); + printf("[%d]nodelist=[%s](%p)\n", + txidx, txinfo[txidx]->nodelist, txinfo[txidx]->nodelist); + printf("[%d]tx_sts_c=[%.*s](%p)\n", + txidx, all_coordinator_cnt, txinfo[txidx]->tx_sts_c, txinfo[txidx]->tx_sts_c); + printf("[%d]tx_sts_m=[%.*s](%p)\n", + txidx, all_mirror_cnt, txinfo[txidx]->tx_sts_m, txinfo[txidx]->tx_sts_m); +} +#endif + + +/*------------------------------------------------------------------------- + * get_txid + * return txid of the transaction at the specified index. + *------------------------------------------------------------------------- + */ +TransactionId +get_txid(int txidx) +{ + return txinfo[txidx]->txid; +} + +/*------------------------------------------------------------------------- + * get_tx + * Return transaction entry which has specified transaction id, or NULL + * if there is no such transaction. + *------------------------------------------------------------------------- + */ +prepared_txninfo * +get_tx(TransactionId txid) +{ + int txloop; + + dispmsg(lvl_debug, "BEGIN: get_tx(%d)", txid); + + for (txlo... [truncated message content] |