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:
101
src/backend/utils/cache/inval.c
vendored
101
src/backend/utils/cache/inval.c
vendored
@@ -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.
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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")));
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
*
|
||||
|
Reference in New Issue
Block a user