1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Two-phase commit. Original patch by Heikki Linnakangas, with additional

hacking by Alvaro Herrera and Tom Lane.
This commit is contained in:
Tom Lane
2005-06-17 22:32:51 +00:00
parent 5495575903
commit d0a89683a3
61 changed files with 4454 additions and 439 deletions

View File

@@ -80,12 +80,13 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.71 2005/04/14 01:38:19 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.72 2005/06/17 22:32:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/twophase_rmgr.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "miscadmin.h"
@@ -171,6 +172,13 @@ static struct CACHECALLBACK
static int cache_callback_count = 0;
/* info values for 2PC callback */
#define TWOPHASE_INFO_MSG 0 /* SharedInvalidationMessage */
#define TWOPHASE_INFO_FILE_BEFORE 1 /* relcache file inval */
#define TWOPHASE_INFO_FILE_AFTER 2 /* relcache file inval */
static void PersistInvalidationMessage(SharedInvalidationMessage *msg);
/* ----------------------------------------------------------------
* Invalidation list support functions
@@ -636,6 +644,56 @@ AtStart_Inval(void)
transInvalInfo->my_level = GetCurrentTransactionNestLevel();
}
/*
* AtPrepare_Inval
* Save the inval lists state at 2PC transaction prepare.
*
* In this phase we just generate 2PC records for all the pending invalidation
* work.
*/
void
AtPrepare_Inval(void)
{
/* Must be at top of stack */
Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL);
/*
* Relcache init file invalidation requires processing both before
* and after we send the SI messages.
*/
if (transInvalInfo->RelcacheInitFileInval)
RegisterTwoPhaseRecord(TWOPHASE_RM_INVAL_ID, TWOPHASE_INFO_FILE_BEFORE,
NULL, 0);
AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
&transInvalInfo->CurrentCmdInvalidMsgs);
ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
PersistInvalidationMessage);
if (transInvalInfo->RelcacheInitFileInval)
RegisterTwoPhaseRecord(TWOPHASE_RM_INVAL_ID, TWOPHASE_INFO_FILE_AFTER,
NULL, 0);
}
/*
* PostPrepare_Inval
* Clean up after successful PREPARE.
*
* Here, we want to act as though the transaction aborted, so that we will
* undo any syscache changes it made, thereby bringing us into sync with the
* outside world, which doesn't believe the transaction committed yet.
*
* If the prepared transaction is later aborted, there is nothing more to
* do; if it commits, we will receive the consequent inval messages just
* like everyone else.
*/
void
PostPrepare_Inval(void)
{
AtEOXact_Inval(false);
}
/*
* AtSubStart_Inval
* Initialize inval lists at start of a subtransaction.
@@ -654,6 +712,47 @@ AtSubStart_Inval(void)
transInvalInfo = myInfo;
}
/*
* PersistInvalidationMessage
* Write an invalidation message to the 2PC state file.
*/
static void
PersistInvalidationMessage(SharedInvalidationMessage *msg)
{
RegisterTwoPhaseRecord(TWOPHASE_RM_INVAL_ID, TWOPHASE_INFO_MSG,
msg, sizeof(SharedInvalidationMessage));
}
/*
* inval_twophase_postcommit
* Process an invalidation message from the 2PC state file.
*/
void
inval_twophase_postcommit(TransactionId xid, uint16 info,
void *recdata, uint32 len)
{
SharedInvalidationMessage *msg;
switch (info)
{
case TWOPHASE_INFO_MSG:
msg = (SharedInvalidationMessage *) recdata;
Assert(len == sizeof(SharedInvalidationMessage));
SendSharedInvalidMessage(msg);
break;
case TWOPHASE_INFO_FILE_BEFORE:
RelationCacheInitFileInvalidate(true);
break;
case TWOPHASE_INFO_FILE_AFTER:
RelationCacheInitFileInvalidate(false);
break;
default:
Assert(false);
break;
}
}
/*
* AtEOXact_Inval
* Process queued-up invalidation messages at end of main transaction.

View File

@@ -22,7 +22,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.7 2005/06/06 17:01:24 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.8 2005/06/17 22:32:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,6 +32,7 @@
#include <unistd.h>
#include "access/heapam.h"
#include "access/twophase_rmgr.h"
#include "catalog/pg_database.h"
#include "catalog/pg_group.h"
#include "catalog/pg_namespace.h"
@@ -48,10 +49,16 @@
#include "utils/syscache.h"
/* Actual names of the flat files (within $PGDATA/global/) */
#define DATABASE_FLAT_FILE "pg_database"
#define GROUP_FLAT_FILE "pg_group"
#define USER_FLAT_FILE "pg_pwd"
/* Info bits in a flatfiles 2PC record */
#define FF_BIT_DATABASE 1
#define FF_BIT_GROUP 2
#define FF_BIT_USER 4
/*
* The need-to-update-files flags are SubTransactionIds that show
@@ -757,6 +764,43 @@ AtEOXact_UpdateFlatFiles(bool isCommit)
SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
}
/*
* This routine is called during transaction prepare.
*
* Record which files need to be refreshed if this transaction later
* commits.
*
* Note: it's OK to clear the flags immediately, since if the PREPARE fails
* further on, we'd only reset the flags anyway. So there's no need for a
* separate PostPrepare call.
*/
void
AtPrepare_UpdateFlatFiles(void)
{
uint16 info = 0;
if (database_file_update_subid != InvalidSubTransactionId)
{
database_file_update_subid = InvalidSubTransactionId;
info |= FF_BIT_DATABASE;
}
if (group_file_update_subid != InvalidSubTransactionId)
{
group_file_update_subid = InvalidSubTransactionId;
info |= FF_BIT_GROUP;
}
if (user_file_update_subid != InvalidSubTransactionId)
{
user_file_update_subid = InvalidSubTransactionId;
info |= FF_BIT_USER;
}
if (info != 0)
RegisterTwoPhaseRecord(TWOPHASE_RM_FLATFILES_ID, info,
NULL, 0);
}
/*
* AtEOSubXact_UpdateFlatFiles
*
@@ -831,3 +875,28 @@ flatfile_update_trigger(PG_FUNCTION_ARGS)
return PointerGetDatum(NULL);
}
/*
* 2PC processing routine for COMMIT PREPARED case.
*
* (We don't have to do anything for ROLLBACK PREPARED.)
*/
void
flatfile_twophase_postcommit(TransactionId xid, uint16 info,
void *recdata, uint32 len)
{
/*
* Set flags to do the needed file updates at the end of my own
* current transaction. (XXX this has some issues if my own
* transaction later rolls back, or if there is any significant
* delay before I commit. OK for now because we disallow
* COMMIT PREPARED inside a transaction block.)
*/
if (info & FF_BIT_DATABASE)
database_file_update_needed();
if (info & FF_BIT_GROUP)
group_file_update_needed();
if (info & FF_BIT_USER)
user_file_update_needed();
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.147 2005/05/19 21:35:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.148 2005/06/17 22:32:47 tgl Exp $
*
*
*-------------------------------------------------------------------------
@@ -232,7 +232,7 @@ InitCommunication(void)
* We're running a postgres bootstrap process or a standalone
* backend. Create private "shmem" and semaphores.
*/
CreateSharedMemoryAndSemaphores(true, MaxBackends, 0);
CreateSharedMemoryAndSemaphores(true, 0);
}
}
@@ -456,7 +456,7 @@ InitPostgres(const char *dbname, const char *username)
*/
if (!am_superuser &&
ReservedBackends > 0 &&
CountEmptyBackendSlots() < ReservedBackends)
!HaveNFreeProcs(ReservedBackends))
ereport(FATAL,
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
errmsg("connection limit exceeded for non-superusers")));

View File

@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.267 2005/06/16 20:47:20 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.268 2005/06/17 22:32:47 tgl Exp $
*
*--------------------------------------------------------------------
*/
@@ -25,6 +25,7 @@
#include "utils/guc.h"
#include "utils/guc_tables.h"
#include "access/twophase.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/async.h"
@@ -1102,6 +1103,15 @@ static struct config_int ConfigureNamesInt[] =
1000, 25, INT_MAX, NULL, NULL
},
{
{"max_prepared_transactions", PGC_POSTMASTER, RESOURCES,
gettext_noop("Sets the maximum number of simultaneously prepared transactions."),
NULL
},
&max_prepared_xacts,
50, 0, 10000, NULL, NULL
},
#ifdef LOCK_DEBUG
{
{"trace_lock_oidmin", PGC_SUSET, DEVELOPER_OPTIONS,

View File

@@ -79,6 +79,7 @@
#shared_buffers = 1000 # min 16, at least max_connections*2, 8KB each
#temp_buffers = 1000 # min 100, 8KB each
#max_prepared_transactions = 50 # 0-10000
#work_mem = 1024 # min 64, size in KB
#maintenance_work_mem = 16384 # min 1024, size in KB
#max_stack_depth = 2048 # min 100, size in KB

View File

@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.80 2005/05/29 04:23:06 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.81 2005/06/17 22:32:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -466,6 +466,48 @@ CommitHoldablePortals(void)
return result;
}
/*
* Pre-prepare processing for portals.
*
* Currently we refuse PREPARE if the transaction created any holdable
* cursors, since it's quite unclear what to do with one. However, this
* has the same API as CommitHoldablePortals and is invoked in the same
* way by xact.c, so that we can easily do something reasonable if anyone
* comes up with something reasonable to do.
*
* Returns TRUE if any holdable cursors were processed, FALSE if not.
*/
bool
PrepareHoldablePortals(void)
{
bool result = false;
HASH_SEQ_STATUS status;
PortalHashEnt *hentry;
hash_seq_init(&status, PortalHashTable);
while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
{
Portal portal = hentry->portal;
/* Is it a holdable portal created in the current xact? */
if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
portal->createSubid != InvalidSubTransactionId &&
portal->status == PORTAL_READY)
{
/*
* We are exiting the transaction that created a holdable
* cursor. Can't do PREPARE.
*/
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot PREPARE a transaction that has created a cursor WITH HOLD")));
}
}
return result;
}
/*
* Pre-commit processing for portals.
*