Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/spock_readonly.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
typedef enum SpockReadonlyMode
{
READONLY_OFF,
READONLY_USER,
READONLY_LOCAL,
READONLY_ALL
} SpockReadonlyMode;

Expand Down
3 changes: 2 additions & 1 deletion src/spock.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ static const struct config_enum_entry exception_logging_options[] = {

static const struct config_enum_entry readonly_options[] = {
{"off", READONLY_OFF, false},
{"user", READONLY_USER, false},
{"local", READONLY_LOCAL, false},
{"user", READONLY_LOCAL, true}, /* backward-compatible alias */
{"all", READONLY_ALL, false},
{NULL, 0, false}
};
Expand Down
49 changes: 40 additions & 9 deletions src/spock_apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -2412,7 +2412,7 @@ replication_handler(StringInfo s)
char action = pq_getmsgbyte(s);

if (spock_readonly == READONLY_ALL)
elog(ERROR, "SPOCK %s: cluster is in read-only mode, not performing replication",
elog(PANIC, "SPOCK %s: cluster is in read-only mode, not performing replication",
MySubscription->name);

memset(&errcallback_arg, 0, sizeof(struct ActionErrCallbackArg));
Expand Down Expand Up @@ -2869,9 +2869,48 @@ apply_work(PGconn *streamConn)
StringInfo msg;
int c;

CHECK_FOR_INTERRUPTS();

if (got_SIGTERM)
break;

if (ConfigReloadPending)
{
ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
}

/*
* Do not apply new transactions if cluster is switched to
* the readonly mode.
*/
if (spock_readonly == READONLY_ALL)
{
/*
* Send feedback to keep walsender alive - we may avoid it
* with introduction of TCP keepalive approach.
*/
maybe_send_feedback(applyconn, last_received,
&last_receive_timestamp);

/*
* In case of an exception we can't break out of the loop
* because exception processing code may also modify the
* database. Wait briefly and continue to the next iteration.
*/
if (xact_had_exception)
{
rc = WaitLatchOrSocket(&MyProc->procLatch,
WL_SOCKET_READABLE | WL_LATCH_SET |
WL_TIMEOUT | WL_POSTMASTER_DEATH,
fd, 1000L);

ResetLatch(&MyProc->procLatch);
continue;
}
break;
}

if (apply_replay_next == NULL)
{
char *buf;
Expand Down Expand Up @@ -2927,12 +2966,6 @@ apply_work(PGconn *streamConn)
queue_append = false;
}

if (ConfigReloadPending)
{
ConfigReloadPending = false;
ProcessConfigFile(PGC_SIGHUP);
}

/* Handle the message received or replayed */
msg = &entry->copydata;
msg->cursor = 0;
Expand Down Expand Up @@ -3050,8 +3083,6 @@ apply_work(PGconn *streamConn)

/* We must not have fallen out of MessageContext by accident */
Assert(CurrentMemoryContext == MessageContext);

CHECK_FOR_INTERRUPTS();
}

if (xact_had_exception)
Expand Down
88 changes: 44 additions & 44 deletions src/spock_readonly.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
* spock_readonly.c
* Spock readonly related functions
*
* Spock readonly functions allow setting the entire cluster to read-only mode,
* preventing INSERT, UPDATE, DELETE, and DDL operations. This file is part of
* pgEdge, Inc. open source project, licensed under the PostgreSQL license.
* Spock readonly functions allow setting the database to read-only mode,
* preventing INSERT, UPDATE, DELETE, and DDL operations.
* In the 'ALL' mode it prevents the database from being modified by spock
* apply workers as well.
* This code employs the transaction_read_only GUC to disable attempts
* to execute DML or DDL commands.
*
* This file is part of pgEdge, Inc. open source project, licensed under
* the PostgreSQL license. For license terms, see the LICENSE file.
Expand Down Expand Up @@ -62,56 +65,53 @@ spockro_terminate_active_transactions(PG_FUNCTION_ARGS)
void
spock_ropost_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
{
bool command_is_ro = false;
/*
* If spock.readonly is set, enforce Postgres core restriction for the
* following query. We actively employ the fact that the core uses the
* XactReadOnly value directly, not through the GetConfigOption function.
* Also, we use this fact here to identify if XactReadOnly has been changed
* by Spock or by external tools.
*/
if (spock_readonly >= READONLY_LOCAL && !superuser())
XactReadOnly = true;
else if (XactReadOnly)
{
const char *value =
GetConfigOption("transaction_read_only", false, false);

switch (query->commandType)
if (strcmp(value, "off") == 0)
/* Spock imposed read-only. Restore the original state. */
XactReadOnly = false;
}
else
{
case CMD_SELECT:
command_is_ro = true;
break;
case CMD_UTILITY:
switch (nodeTag(query->utilityStmt))
{
case T_AlterSystemStmt:
case T_DeallocateStmt:
case T_ExecuteStmt:
case T_ExplainStmt:
case T_PrepareStmt:
case T_TransactionStmt:
case T_VariableSetStmt:
case T_VariableShowStmt:
command_is_ro = true;
break;
default:
command_is_ro = false;
break;
}
break;
default:
command_is_ro = false;
break;
/* XactReadOnly is already false, nothing to restore. */
}
if (spock_readonly >= READONLY_USER && !command_is_ro)
ereport(ERROR, (errmsg("spock: invalid statement for a read-only cluster")));
}

void
spock_roExecutorStart(QueryDesc *queryDesc, int eflags)
{
bool command_is_ro = false;
/*
* Let's do the same job as at parse analysis hook.
*
* In some cases parse analysis and planning may be skipped on repeated
* execution (remember SPI plan for example). So, additional control makes
* sense here.
*/
if (spock_readonly >= READONLY_LOCAL && !superuser())
XactReadOnly = true;
else if (XactReadOnly)
{
const char *value =
GetConfigOption("transaction_read_only", false, false);

switch (queryDesc->operation)
if (strcmp(value, "off") == 0)
/* Spock imposed read-only. Restore the original state. */
XactReadOnly = false;
}
else
{
case CMD_SELECT:
command_is_ro = true;
break;
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
default:
command_is_ro = false;
break;
/* XactReadOnly is already false, nothing to restore. */
}
if (spock_readonly >= READONLY_USER && !command_is_ro)
ereport(ERROR, (errmsg("spock: invalid statement for a read-only cluster")));
}