mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-16 02:23:49 +08:00
Fix server detach/delete corner cases
Prevent server delete if the server contains data, unless user specifies `force => true`. In case the server is the only data replica, we don't allow delete/detach unless table/chunks are dropped. The idea is to have the same semantics for delete as for detach since delete actually calls detach We also try to update pg_foreign_table when we delete server if there is another server containing the same chunk. An internal function is added to enable updating foreign table server which might be useful in some cases since foreign table server is considered a default server for that particular chunk. Since this command needs to work even if the server we're trying to remove is non responsive, we're not removing any data on the remote data node.
This commit is contained in:
parent
b2fde83d2e
commit
0da34e840e
@ -178,7 +178,8 @@ AS '@MODULE_PATHNAME@', 'ts_server_add' LANGUAGE C VOLATILE;
|
||||
CREATE OR REPLACE FUNCTION delete_server(
|
||||
server_name NAME,
|
||||
if_exists BOOLEAN = FALSE,
|
||||
cascade BOOLEAN = FALSE
|
||||
cascade BOOLEAN = FALSE,
|
||||
force BOOLEAN = FALSE
|
||||
) RETURNS BOOLEAN AS '@MODULE_PATHNAME@', 'ts_server_delete' LANGUAGE C VOLATILE;
|
||||
|
||||
-- Attach a server to a hypertable
|
||||
|
@ -5,3 +5,7 @@
|
||||
-- Check if server is up
|
||||
CREATE FUNCTION _timescaledb_internal.server_ping(server_name NAME) RETURNS BOOLEAN
|
||||
AS '@MODULE_PATHNAME@', 'ts_server_ping' LANGUAGE C VOLATILE;
|
||||
|
||||
-- change default server for a chunk
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.set_chunk_default_server(schema_name NAME, chunk_table_name NAME, server_name NAME) RETURNS BOOLEAN
|
||||
AS '@MODULE_PATHNAME@', 'ts_set_chunk_default_server' LANGUAGE C VOLATILE;
|
||||
|
@ -120,9 +120,10 @@ extern List *ts_chunk_find_all_oids(Hypertable *ht, List *dimension_vecs, LOCKMO
|
||||
extern TSDLLEXPORT int ts_chunk_add_constraints(Chunk *chunk);
|
||||
|
||||
extern Chunk *ts_chunk_copy(Chunk *chunk);
|
||||
extern Chunk *ts_chunk_get_by_name_with_memory_context(const char *schema_name,
|
||||
const char *table_name, MemoryContext mctx,
|
||||
bool fail_if_not_found);
|
||||
extern TSDLLEXPORT Chunk *ts_chunk_get_by_name_with_memory_context(const char *schema_name,
|
||||
const char *table_name,
|
||||
MemoryContext mctx,
|
||||
bool fail_if_not_found);
|
||||
extern TSDLLEXPORT void ts_chunk_insert_lock(Chunk *chunk, LOCKMODE lock);
|
||||
|
||||
extern TSDLLEXPORT Oid ts_chunk_create_table(Chunk *chunk, Hypertable *ht,
|
||||
|
@ -4,8 +4,14 @@
|
||||
* LICENSE-APACHE for a copy of the license.
|
||||
*/
|
||||
#include <postgres.h>
|
||||
#include <catalog/pg_foreign_table.h>
|
||||
#include <catalog/pg_foreign_server.h>
|
||||
#include <catalog/dependency.h>
|
||||
#include <foreign/foreign.h>
|
||||
#include <utils/builtins.h>
|
||||
#include <utils/syscache.h>
|
||||
#include <utils/inval.h>
|
||||
#include <access/xact.h>
|
||||
|
||||
#include "chunk_server.h"
|
||||
#include "scanner.h"
|
||||
@ -223,6 +229,17 @@ ts_chunk_server_delete_by_chunk_id(int32 chunk_id)
|
||||
CurrentMemoryContext);
|
||||
}
|
||||
|
||||
int
|
||||
ts_chunk_server_delete_by_chunk_id_and_server_name(int32 chunk_id, const char *server_name)
|
||||
{
|
||||
return ts_chunk_server_scan_by_chunk_id_and_server_internal(chunk_id,
|
||||
server_name,
|
||||
chunk_server_tuple_delete,
|
||||
NULL,
|
||||
RowExclusiveLock,
|
||||
CurrentMemoryContext);
|
||||
}
|
||||
|
||||
int
|
||||
ts_chunk_server_delete_by_servername(const char *servername)
|
||||
{
|
||||
@ -250,9 +267,108 @@ ts_chunk_server_scan_by_servername_and_hypertable_id(const char *server_name, in
|
||||
int32 chunk_id = lfirst_int(lc);
|
||||
ChunkServer *cs =
|
||||
ts_chunk_server_scan_by_chunk_id_and_servername(chunk_id, server_name, mctx);
|
||||
results = lappend(results, cs);
|
||||
if (cs != NULL)
|
||||
results = lappend(results, cs);
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(old);
|
||||
return results;
|
||||
}
|
||||
|
||||
bool
|
||||
ts_chunk_server_contains_non_replicated_chunks(List *chunk_servers)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
foreach (lc, chunk_servers)
|
||||
{
|
||||
ChunkServer *cs = lfirst(lc);
|
||||
List *replicas = ts_chunk_server_scan_by_chunk_id(cs->fd.chunk_id, CurrentMemoryContext);
|
||||
if (list_length(replicas) < 2)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ts_chunk_server_update_foreign_table_server(Oid relid, Oid new_server_id)
|
||||
{
|
||||
Relation ftrel;
|
||||
HeapTuple tuple;
|
||||
HeapTuple copy;
|
||||
Datum values[Natts_pg_foreign_table];
|
||||
bool nulls[Natts_pg_foreign_table];
|
||||
CatalogSecurityContext sec_ctx;
|
||||
Oid old_server_id;
|
||||
long updated;
|
||||
|
||||
tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_TABLE),
|
||||
errmsg("relation with OID %u does not exist", relid)));
|
||||
|
||||
ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
|
||||
|
||||
heap_deform_tuple(tuple, RelationGetDescr(ftrel), values, nulls);
|
||||
|
||||
old_server_id =
|
||||
DatumGetObjectId(values[AttrNumberGetAttrOffset(Anum_pg_foreign_table_ftserver)]);
|
||||
|
||||
values[AttrNumberGetAttrOffset(Anum_pg_foreign_table_ftserver)] =
|
||||
ObjectIdGetDatum(new_server_id);
|
||||
|
||||
copy = heap_form_tuple(RelationGetDescr(ftrel), values, nulls);
|
||||
|
||||
ts_catalog_database_info_become_owner(ts_catalog_database_info_get(), &sec_ctx);
|
||||
ts_catalog_update_tid(ftrel, &tuple->t_self, copy);
|
||||
ts_catalog_restore_user(&sec_ctx);
|
||||
|
||||
table_close(ftrel, RowExclusiveLock);
|
||||
heap_freetuple(copy);
|
||||
ReleaseSysCache(tuple);
|
||||
/* invalidate foreign table cache */
|
||||
CacheInvalidateRelcacheByRelid(ForeignTableRelationId);
|
||||
/* update dependencies between foreign table and foreign server */
|
||||
updated = changeDependencyFor(RelationRelationId,
|
||||
relid,
|
||||
ForeignServerRelationId,
|
||||
old_server_id,
|
||||
new_server_id);
|
||||
if (updated != 1)
|
||||
elog(ERROR,
|
||||
"failed while trying to update server for foreign table %s",
|
||||
get_rel_name(relid));
|
||||
|
||||
/* make changes visible */
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
void
|
||||
ts_chunk_server_update_foreign_table_server_if_needed(int32 chunk_id, Oid existing_server_id)
|
||||
{
|
||||
ListCell *lc;
|
||||
ChunkServer *new_server = NULL;
|
||||
Chunk *chunk = ts_chunk_get_by_id(chunk_id, true);
|
||||
ForeignTable *foreign_table = NULL;
|
||||
|
||||
Assert(chunk->relkind == RELKIND_FOREIGN_TABLE);
|
||||
foreign_table = GetForeignTable(chunk->table_id);
|
||||
|
||||
/* no need to update since foreign table doesn't reference server we try to remove */
|
||||
if (existing_server_id != foreign_table->serverid)
|
||||
return;
|
||||
|
||||
Assert(list_length(chunk->servers) > 1);
|
||||
|
||||
foreach (lc, chunk->servers)
|
||||
{
|
||||
new_server = lfirst(lc);
|
||||
if (new_server->foreign_server_oid != existing_server_id)
|
||||
break;
|
||||
}
|
||||
Assert(new_server != NULL);
|
||||
ts_chunk_server_update_foreign_table_server(chunk->table_id, new_server->foreign_server_oid);
|
||||
}
|
||||
|
@ -22,9 +22,16 @@ ts_chunk_server_scan_by_chunk_id_and_servername(int32 chunk_id, const char *serv
|
||||
extern TSDLLEXPORT void ts_chunk_server_insert(ChunkServer *server);
|
||||
extern void ts_chunk_server_insert_multi(List *chunk_servers);
|
||||
extern int ts_chunk_server_delete_by_chunk_id(int32 chunk_id);
|
||||
extern TSDLLEXPORT int ts_chunk_server_delete_by_chunk_id_and_server_name(int32 chunk_id,
|
||||
const char *server_name);
|
||||
extern int ts_chunk_server_delete_by_servername(const char *servername);
|
||||
extern TSDLLEXPORT List *
|
||||
ts_chunk_server_scan_by_servername_and_hypertable_id(const char *server_name, int32 hypertable_id,
|
||||
MemoryContext mctx);
|
||||
extern TSDLLEXPORT bool ts_chunk_server_contains_non_replicated_chunks(List *chunk_servers);
|
||||
|
||||
extern TSDLLEXPORT void ts_chunk_server_update_foreign_table_server(Oid relid, Oid new_server_id);
|
||||
extern TSDLLEXPORT void
|
||||
ts_chunk_server_update_foreign_table_server_if_needed(int32 chunk_id, Oid existing_server_id);
|
||||
|
||||
#endif /* TIMESCALEDB_CHUNK_SERVER_H */
|
||||
|
@ -52,6 +52,7 @@ TS_FUNCTION_INFO_V1(ts_server_ping);
|
||||
TS_FUNCTION_INFO_V1(ts_server_detach);
|
||||
TS_FUNCTION_INFO_V1(ts_server_block_new_chunks);
|
||||
TS_FUNCTION_INFO_V1(ts_server_allow_new_chunks);
|
||||
TS_FUNCTION_INFO_V1(ts_set_chunk_default_server);
|
||||
TS_FUNCTION_INFO_V1(ts_timescaledb_fdw_handler);
|
||||
TS_FUNCTION_INFO_V1(ts_timescaledb_fdw_validator);
|
||||
TS_FUNCTION_INFO_V1(ts_remote_txn_id_in);
|
||||
@ -163,6 +164,12 @@ ts_server_allow_new_chunks(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_DATUM(ts_cm_functions->server_set_block_new_chunks(fcinfo, false));
|
||||
}
|
||||
|
||||
Datum
|
||||
ts_set_chunk_default_server(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_DATUM(ts_cm_functions->set_chunk_default_server(fcinfo));
|
||||
}
|
||||
|
||||
Datum
|
||||
ts_timescaledb_fdw_handler(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@ -596,6 +603,7 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = {
|
||||
.server_ping = error_no_default_fn_pg_community,
|
||||
.detach_server = error_no_default_fn_pg_community,
|
||||
.server_set_block_new_chunks = server_set_block_new_chunks_default,
|
||||
.set_chunk_default_server = error_no_default_fn_pg_community,
|
||||
.show_chunk = error_no_default_fn_pg_community,
|
||||
.create_chunk = error_no_default_fn_pg_community,
|
||||
.create_chunk_on_servers = create_chunk_on_servers_default,
|
||||
|
@ -116,6 +116,7 @@ typedef struct CrossModuleFunctions
|
||||
PGFunction server_ping;
|
||||
PGFunction detach_server;
|
||||
Datum (*server_set_block_new_chunks)(PG_FUNCTION_ARGS, bool block);
|
||||
PGFunction set_chunk_default_server;
|
||||
PGFunction create_chunk;
|
||||
PGFunction show_chunk;
|
||||
List *(*get_servername_list)(void);
|
||||
|
@ -54,3 +54,4 @@
|
||||
#define ERRCODE_TS_UNEXPECTED MAKE_SQLSTATE('T', 'S', '5', '0', '1')
|
||||
#define ERRCODE_TS_COMMUNICATION_ERROR MAKE_SQLSTATE('T', 'S', '5', '0', '2')
|
||||
#define ERRCODE_TS_CHUNK_COLLISION MAKE_SQLSTATE('T', 'S', '5', '0', '3')
|
||||
#define ERRCODE_TS_SERVER_IN_USE MAKE_SQLSTATE('T', 'S', '5', '0', '4')
|
||||
|
@ -2521,11 +2521,11 @@ ts_hypertable_get_servername_list(Hypertable *ht)
|
||||
}
|
||||
|
||||
List *
|
||||
ts_hypertable_get_available_servers(Hypertable *ht, bool error)
|
||||
ts_hypertable_get_available_servers(Hypertable *ht, bool error_if_missing)
|
||||
{
|
||||
List *available_servers =
|
||||
get_hypertable_server_values(ht, filter_non_blocked_servers, get_hypertable_server);
|
||||
if (available_servers == NIL && error)
|
||||
if (available_servers == NIL && error_if_missing)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_TS_NO_SERVERS),
|
||||
(errmsg("no available servers (detached or blocked for new chunks) for "
|
||||
|
@ -147,7 +147,7 @@ extern TSDLLEXPORT void ts_hypertable_clone_constraints_to_compressed(Hypertable
|
||||
extern List *ts_hypertable_assign_chunk_servers(Hypertable *ht, Hypercube *cube);
|
||||
extern TSDLLEXPORT List *ts_hypertable_get_servername_list(Hypertable *ht);
|
||||
extern TSDLLEXPORT List *ts_hypertable_get_serverids_list(Hypertable *ht);
|
||||
extern TSDLLEXPORT List *ts_hypertable_get_available_servers(Hypertable *ht, bool error);
|
||||
extern TSDLLEXPORT List *ts_hypertable_get_available_servers(Hypertable *ht, bool error_if_missing);
|
||||
extern TSDLLEXPORT List *ts_hypertable_get_available_server_oids(Hypertable *ht);
|
||||
extern TSDLLEXPORT HypertableType ts_hypertable_get_type(Hypertable *ht);
|
||||
|
||||
|
@ -229,6 +229,7 @@ CrossModuleFunctions tsl_cm_functions = {
|
||||
.attach_server = error_not_supported_default_fn,
|
||||
.detach_server = error_not_supported_default_fn,
|
||||
.server_set_block_new_chunks = error_server_set_block_new_chunks_not_supported,
|
||||
.set_chunk_default_server = error_not_supported_default_fn,
|
||||
.show_chunk = error_not_supported_default_fn,
|
||||
.create_chunk = error_not_supported_default_fn,
|
||||
.create_chunk_on_servers = error_create_chunk_on_servers_not_supported,
|
||||
@ -255,6 +256,7 @@ CrossModuleFunctions tsl_cm_functions = {
|
||||
.server_ping = server_ping,
|
||||
.detach_server = server_detach,
|
||||
.server_set_block_new_chunks = server_set_block_new_chunks,
|
||||
.set_chunk_default_server = server_set_chunk_default_server,
|
||||
.show_chunk = chunk_show,
|
||||
.create_chunk = chunk_create,
|
||||
.create_chunk_on_servers = chunk_api_create_on_servers,
|
||||
|
292
tsl/src/server.c
292
tsl/src/server.c
@ -10,12 +10,15 @@
|
||||
#include <nodes/makefuncs.h>
|
||||
#include <nodes/parsenodes.h>
|
||||
#include <catalog/pg_foreign_server.h>
|
||||
#include <catalog/pg_foreign_table.h>
|
||||
#include <catalog/namespace.h>
|
||||
#include <catalog/pg_namespace.h>
|
||||
#include <commands/dbcommands.h>
|
||||
#include <commands/defrem.h>
|
||||
#include <commands/event_trigger.h>
|
||||
#include <utils/builtins.h>
|
||||
#include <utils/inval.h>
|
||||
#include <utils/syscache.h>
|
||||
#include <libpq/crypt.h>
|
||||
#include <miscadmin.h>
|
||||
#include <funcapi.h>
|
||||
@ -29,6 +32,7 @@
|
||||
#if PG_VERSION_SUPPORTS_MULTINODE
|
||||
#include "remote/async.h"
|
||||
#include "remote/connection.h"
|
||||
#include "remote/connection_cache.h"
|
||||
#endif
|
||||
#include "server.h"
|
||||
#include "hypertable.h"
|
||||
@ -607,77 +611,6 @@ server_add_without_dist_id(PG_FUNCTION_ARGS)
|
||||
return server_add_internal(fcinfo, false);
|
||||
}
|
||||
|
||||
Datum
|
||||
server_delete(PG_FUNCTION_ARGS)
|
||||
{
|
||||
const char *servername = PG_ARGISNULL(0) ? NULL : PG_GETARG_CSTRING(0);
|
||||
bool if_exists = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
|
||||
bool cascade = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
|
||||
ForeignServer *server = GetForeignServerByName(servername, if_exists);
|
||||
bool deleted = false;
|
||||
|
||||
if (NULL != server)
|
||||
{
|
||||
DropStmt stmt = {
|
||||
.type = T_DropStmt,
|
||||
#if PG96
|
||||
.objects = list_make1(list_make1(makeString(pstrdup(servername)))),
|
||||
#else
|
||||
.objects = list_make1(makeString(pstrdup(servername))),
|
||||
#endif
|
||||
.removeType = OBJECT_FOREIGN_SERVER,
|
||||
.behavior = cascade ? DROP_CASCADE : DROP_RESTRICT,
|
||||
.missing_ok = if_exists,
|
||||
};
|
||||
ObjectAddress address = {
|
||||
.classId = ForeignServerRelationId,
|
||||
.objectId = server->serverid,
|
||||
.objectSubId = 0,
|
||||
};
|
||||
ObjectAddress secondary_object = {
|
||||
.classId = InvalidOid,
|
||||
.objectId = InvalidOid,
|
||||
.objectSubId = 0,
|
||||
};
|
||||
Node *parsetree = (Node *) &stmt;
|
||||
|
||||
/* Make sure event triggers are invoked so that all dropped objects
|
||||
* are collected during a cascading drop. This ensures all dependent
|
||||
* objects get cleaned up. */
|
||||
EventTriggerBeginCompleteQuery();
|
||||
|
||||
#if !PG96
|
||||
remove_distributed_id_from_backend(servername);
|
||||
#endif
|
||||
|
||||
PG_TRY();
|
||||
{
|
||||
EventTriggerDDLCommandStart(parsetree);
|
||||
RemoveObjects(&stmt);
|
||||
EventTriggerCollectSimpleCommand(address, secondary_object, parsetree);
|
||||
EventTriggerSQLDrop(parsetree);
|
||||
EventTriggerDDLCommandEnd(parsetree);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
EventTriggerEndCompleteQuery();
|
||||
PG_RE_THROW();
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
#if !PG96
|
||||
/* Remove self from dist db if no longer have backends */
|
||||
if (server_get_servername_list() == NIL)
|
||||
dist_util_remove_from_db();
|
||||
#endif
|
||||
|
||||
EventTriggerEndCompleteQuery();
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
PG_RETURN_BOOL(deleted);
|
||||
}
|
||||
|
||||
Datum
|
||||
server_attach(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@ -734,11 +667,36 @@ server_attach(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_DATUM(create_hypertable_server_datum(fcinfo, (HypertableServer *) linitial(result)));
|
||||
}
|
||||
|
||||
/* Only used for generating proper error message */
|
||||
typedef enum OperationType
|
||||
{
|
||||
OP_BLOCK,
|
||||
OP_DETACH,
|
||||
OP_DELETE
|
||||
} OperationType;
|
||||
|
||||
static char *
|
||||
get_operation_type_message(OperationType op_type)
|
||||
{
|
||||
switch (op_type)
|
||||
{
|
||||
case OP_BLOCK:
|
||||
return "blocking new chunks on";
|
||||
case OP_DETACH:
|
||||
return "detaching";
|
||||
case OP_DELETE:
|
||||
return "deleting";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_replication(const char *server_name, Hypertable *ht, bool force, bool detach)
|
||||
check_replication_for_new_data(const char *server_name, Hypertable *ht, bool force,
|
||||
OperationType op_type)
|
||||
{
|
||||
List *available_servers = ts_hypertable_get_available_servers(ht, false);
|
||||
char *operation = detach ? "detaching" : "blocking new chunks on";
|
||||
char *operation = get_operation_type_message(op_type);
|
||||
|
||||
if (ht->fd.replication_factor < list_length(available_servers))
|
||||
return;
|
||||
@ -762,26 +720,57 @@ check_replication(const char *server_name, Hypertable *ht, bool force, bool deta
|
||||
server_name)));
|
||||
}
|
||||
|
||||
static void
|
||||
server_detach_validate(const char *server_name, Hypertable *ht, bool force)
|
||||
static List *
|
||||
server_detach_validate(const char *server_name, Hypertable *ht, bool force, OperationType op_type)
|
||||
{
|
||||
List *chunk_servers =
|
||||
ts_chunk_server_scan_by_servername_and_hypertable_id(server_name,
|
||||
ht->fd.id,
|
||||
CurrentMemoryContext);
|
||||
bool has_non_replicated_chunks = ts_chunk_server_contains_non_replicated_chunks(chunk_servers);
|
||||
char *operation = get_operation_type_message(op_type);
|
||||
|
||||
if (list_length(chunk_servers) > 0)
|
||||
if (has_non_replicated_chunks)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_TS_INTERNAL_ERROR),
|
||||
errmsg("server \"%s\" cannot be detached because it contains chunks",
|
||||
server_name)));
|
||||
errmsg("%s server \"%s\" would mean a data-loss for hypertable "
|
||||
"\"%s\" since server has the only data replica",
|
||||
operation,
|
||||
server_name,
|
||||
NameStr(ht->fd.table_name)),
|
||||
errhint("Ensure the server \"%s\" has no non-replicated data before %s it.",
|
||||
server_name,
|
||||
operation)));
|
||||
|
||||
check_replication(server_name, ht, force, true);
|
||||
if (list_length(chunk_servers) > 0)
|
||||
{
|
||||
if (force)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING),
|
||||
errmsg("hypertable \"%s\" has under-replicated chunks due to %s "
|
||||
"server \"%s\"",
|
||||
NameStr(ht->fd.table_name),
|
||||
operation,
|
||||
server_name)));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_TS_SERVER_IN_USE),
|
||||
errmsg("%s server \"%s\" failed because it contains chunks "
|
||||
"for hypertable \"%s\"",
|
||||
operation,
|
||||
server_name,
|
||||
NameStr(ht->fd.table_name))));
|
||||
}
|
||||
|
||||
check_replication_for_new_data(server_name, ht, force, op_type);
|
||||
|
||||
return chunk_servers;
|
||||
}
|
||||
|
||||
static int
|
||||
server_modify_hypertable_servers(const char *server_name, List *hypertable_servers,
|
||||
bool all_hypertables, bool detach, bool block_chunks, bool force)
|
||||
bool all_hypertables, OperationType op_type, bool block_chunks,
|
||||
bool force)
|
||||
{
|
||||
Cache *hcache = ts_hypertable_cache_pin();
|
||||
ListCell *lc;
|
||||
@ -806,10 +795,24 @@ server_modify_hypertable_servers(const char *server_name, List *hypertable_serve
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("permission denied for hypertable \"%s\"", get_rel_name(relid))));
|
||||
else if (detach)
|
||||
else if (op_type == OP_DETACH || op_type == OP_DELETE)
|
||||
{
|
||||
/* we have permissions to detach */
|
||||
server_detach_validate(NameStr(server->fd.server_name), ht, force);
|
||||
List *chunk_servers =
|
||||
server_detach_validate(NameStr(server->fd.server_name), ht, force, op_type);
|
||||
ListCell *cs_lc;
|
||||
|
||||
/* update chunk foreign table server and delete chunk mapping */
|
||||
foreach (cs_lc, chunk_servers)
|
||||
{
|
||||
ChunkServer *cs = lfirst(cs_lc);
|
||||
ts_chunk_server_update_foreign_table_server_if_needed(cs->fd.chunk_id,
|
||||
cs->foreign_server_oid);
|
||||
ts_chunk_server_delete_by_chunk_id_and_server_name(cs->fd.chunk_id,
|
||||
NameStr(cs->fd.server_name));
|
||||
}
|
||||
|
||||
/* delete hypertable mapping */
|
||||
removed +=
|
||||
ts_hypertable_server_delete_by_servername_and_hypertable_id(server_name, ht->fd.id);
|
||||
}
|
||||
@ -829,7 +832,7 @@ server_modify_hypertable_servers(const char *server_name, List *hypertable_serve
|
||||
continue;
|
||||
}
|
||||
|
||||
check_replication(server_name, ht, force, false);
|
||||
check_replication_for_new_data(server_name, ht, force, OP_BLOCK);
|
||||
}
|
||||
server->fd.block_chunks = block_chunks;
|
||||
removed += ts_hypertable_server_update(server);
|
||||
@ -846,19 +849,19 @@ server_block_hypertable_servers(const char *server_name, List *hypertable_server
|
||||
return server_modify_hypertable_servers(server_name,
|
||||
hypertable_servers,
|
||||
all_hypertables,
|
||||
false,
|
||||
OP_BLOCK,
|
||||
block_chunks,
|
||||
force);
|
||||
}
|
||||
|
||||
static int
|
||||
server_detach_hypertable_servers(const char *server_name, List *hypertable_servers,
|
||||
bool all_hypertables, bool force)
|
||||
bool all_hypertables, bool force, OperationType op_type)
|
||||
{
|
||||
return server_modify_hypertable_servers(server_name,
|
||||
hypertable_servers,
|
||||
all_hypertables,
|
||||
true,
|
||||
op_type,
|
||||
false,
|
||||
force);
|
||||
}
|
||||
@ -951,11 +954,111 @@ server_detach(PG_FUNCTION_ARGS)
|
||||
hypertable_servers =
|
||||
ts_hypertable_server_scan_by_server_name(server_name, CurrentMemoryContext);
|
||||
|
||||
removed =
|
||||
server_detach_hypertable_servers(server_name, hypertable_servers, all_hypertables, force);
|
||||
removed = server_detach_hypertable_servers(server_name,
|
||||
hypertable_servers,
|
||||
all_hypertables,
|
||||
force,
|
||||
OP_DETACH);
|
||||
PG_RETURN_INT32(removed);
|
||||
}
|
||||
|
||||
Datum
|
||||
server_delete(PG_FUNCTION_ARGS)
|
||||
{
|
||||
const char *server_name = PG_ARGISNULL(0) ? NULL : PG_GETARG_CSTRING(0);
|
||||
bool if_exists = PG_ARGISNULL(1) ? false : PG_GETARG_BOOL(1);
|
||||
bool cascade = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2);
|
||||
bool force = PG_ARGISNULL(3) ? false : PG_GETARG_BOOL(3);
|
||||
ForeignServer *server = GetForeignServerByName(server_name, if_exists);
|
||||
List *hypertable_servers = NIL;
|
||||
DropStmt stmt;
|
||||
ObjectAddress address;
|
||||
ObjectAddress secondary_object = {
|
||||
.classId = InvalidOid,
|
||||
.objectId = InvalidOid,
|
||||
.objectSubId = 0,
|
||||
};
|
||||
Node *parsetree = NULL;
|
||||
#if !PG96
|
||||
UserMapping *um = NULL;
|
||||
Cache *conn_cache;
|
||||
#endif
|
||||
|
||||
if (server_name == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid server_name: cannot be NULL")));
|
||||
|
||||
if (server == NULL)
|
||||
PG_RETURN_BOOL(false);
|
||||
|
||||
#if !PG96
|
||||
um = get_user_mapping(GetUserId(), server->serverid);
|
||||
if (um != NULL)
|
||||
{
|
||||
conn_cache = remote_connection_cache_pin();
|
||||
remote_connection_cache_remove(conn_cache, um);
|
||||
ts_cache_release(conn_cache);
|
||||
}
|
||||
#endif
|
||||
/* detach server */
|
||||
hypertable_servers =
|
||||
ts_hypertable_server_scan_by_server_name(server_name, CurrentMemoryContext);
|
||||
|
||||
server_detach_hypertable_servers(server_name, hypertable_servers, true, force, OP_DELETE);
|
||||
|
||||
stmt = (DropStmt)
|
||||
{
|
||||
.type = T_DropStmt,
|
||||
#if PG96
|
||||
.objects = list_make1(list_make1(makeString(pstrdup(server_name)))),
|
||||
#else
|
||||
.objects = list_make1(makeString(pstrdup(server_name))),
|
||||
#endif
|
||||
.removeType = OBJECT_FOREIGN_SERVER, .behavior = cascade ? DROP_CASCADE : DROP_RESTRICT,
|
||||
.missing_ok = if_exists,
|
||||
};
|
||||
|
||||
parsetree = (Node *) &stmt;
|
||||
|
||||
/* Make sure event triggers are invoked so that all dropped objects
|
||||
* are collected during a cascading drop. This ensures all dependent
|
||||
* objects get cleaned up. */
|
||||
EventTriggerBeginCompleteQuery();
|
||||
|
||||
#if !PG96
|
||||
remove_distributed_id_from_backend(server_name);
|
||||
#endif
|
||||
|
||||
PG_TRY();
|
||||
{
|
||||
ObjectAddressSet(address, ForeignServerRelationId, server->serverid);
|
||||
EventTriggerDDLCommandStart(parsetree);
|
||||
RemoveObjects(&stmt);
|
||||
EventTriggerCollectSimpleCommand(address, secondary_object, parsetree);
|
||||
EventTriggerSQLDrop(parsetree);
|
||||
EventTriggerDDLCommandEnd(parsetree);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
EventTriggerEndCompleteQuery();
|
||||
PG_RE_THROW();
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
#if !PG96
|
||||
/* Remove self from dist db if no longer have backends */
|
||||
if (server_get_servername_list() == NIL)
|
||||
dist_util_remove_from_db();
|
||||
#endif
|
||||
|
||||
EventTriggerEndCompleteQuery();
|
||||
CommandCounterIncrement();
|
||||
CacheInvalidateRelcacheByRelid(ForeignServerRelationId);
|
||||
|
||||
PG_RETURN_BOOL(true);
|
||||
}
|
||||
|
||||
List *
|
||||
server_get_servername_list(void)
|
||||
{
|
||||
@ -1042,3 +1145,16 @@ server_ping(PG_FUNCTION_ARGS)
|
||||
(errmsg("server ping is only supported on PG10 and above"))));
|
||||
#endif
|
||||
}
|
||||
|
||||
Datum
|
||||
server_set_chunk_default_server(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *schema_name = PG_ARGISNULL(0) ? NULL : PG_GETARG_CSTRING(0);
|
||||
char *table_name = PG_ARGISNULL(1) ? NULL : PG_GETARG_CSTRING(1);
|
||||
char *server_name = PG_ARGISNULL(2) ? NULL : PG_GETARG_CSTRING(2);
|
||||
ForeignServer *server = GetForeignServerByName(server_name, false);
|
||||
Chunk *chunk = chunk_get_by_name(schema_name, table_name, true);
|
||||
|
||||
ts_chunk_server_update_foreign_table_server(chunk->table_id, server->serverid);
|
||||
PG_RETURN_BOOL(true);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ extern Datum server_detach(PG_FUNCTION_ARGS);
|
||||
extern Datum server_set_block_new_chunks(PG_FUNCTION_ARGS, bool block);
|
||||
extern List *server_get_servername_list(void);
|
||||
extern Datum server_ping(PG_FUNCTION_ARGS);
|
||||
extern Datum server_set_chunk_default_server(PG_FUNCTION_ARGS);
|
||||
|
||||
/* This should only be used for testing */
|
||||
extern Datum server_add_without_dist_id(PG_FUNCTION_ARGS);
|
||||
|
@ -389,6 +389,15 @@ SELECT * FROM test.show_subtables('disttable');
|
||||
_timescaledb_internal._hyper_3_4_dist_chunk |
|
||||
(2 rows)
|
||||
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
foreign_table_name | foreign_server_name
|
||||
-----------------------+---------------------
|
||||
_hyper_3_3_dist_chunk | server_4
|
||||
_hyper_3_4_dist_chunk | server_2
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.chunk;
|
||||
id | hypertable_id | schema_name | table_name | compressed_chunk_id | dropped
|
||||
----+---------------+-----------------------+-----------------------+---------------------+---------
|
||||
@ -405,14 +414,51 @@ SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
4 | 3 | server_4
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server('_timescaledb_internal', '_hyper_3_3_dist_chunk', 'server_2');
|
||||
set_chunk_default_server
|
||||
--------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
foreign_table_name | foreign_server_name
|
||||
-----------------------+---------------------
|
||||
_hyper_3_3_dist_chunk | server_2
|
||||
_hyper_3_4_dist_chunk | server_2
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server('_timescaledb_internal', '_hyper_3_3_dist_chunk', 'server_4');
|
||||
set_chunk_default_server
|
||||
--------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- Will fail because server_2 contains chunks
|
||||
SELECT * FROM delete_server('server_2', cascade => true);
|
||||
ERROR: deleting server "server_2" failed because it contains chunks for hypertable "disttable"
|
||||
-- non-existing chunk
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server('x', 'x_chunk', 'server_4');
|
||||
ERROR: chunk not found
|
||||
-- non-existing server
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server('_timescaledb_internal', '_hyper_3_3_dist_chunk', 'server_0000');
|
||||
ERROR: server "server_0000" does not exist
|
||||
-- NULL try
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server(NULL, NULL, 'server_4');
|
||||
ERROR: chunk not found
|
||||
\set ON_ERROR_STOP 1
|
||||
-- Deleting a server removes the "foreign" chunk table(s) that
|
||||
-- reference that server as "primary" and should also remove the
|
||||
-- hypertable_server and chunk_server mappings for that server. In
|
||||
-- the future we might want to fallback to a replica server for those
|
||||
-- chunks that have multiple servers so that the chunk is not removed
|
||||
-- unnecessarily.
|
||||
SELECT * FROM delete_server('server_2', cascade => true);
|
||||
NOTICE: drop cascades to 2 other objects
|
||||
-- unnecessarily. We use force => true b/c server_2 contains chunks.
|
||||
SELECT * FROM delete_server('server_2', cascade => true, force => true);
|
||||
WARNING: hypertable "disttable" has under-replicated chunks due to deleting server "server_2"
|
||||
WARNING: new data for hypertable "disttable" will be under-replicated due to deleting server "server_2"
|
||||
NOTICE: drop cascades to user mapping for super_user on server server_2
|
||||
delete_server
|
||||
---------------
|
||||
t
|
||||
@ -422,13 +468,24 @@ SELECT * FROM test.show_subtables('disttable');
|
||||
Child | Tablespace
|
||||
---------------------------------------------+------------
|
||||
_timescaledb_internal._hyper_3_3_dist_chunk |
|
||||
(1 row)
|
||||
_timescaledb_internal._hyper_3_4_dist_chunk |
|
||||
(2 rows)
|
||||
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
foreign_table_name | foreign_server_name
|
||||
-----------------------+---------------------
|
||||
_hyper_3_3_dist_chunk | server_4
|
||||
_hyper_3_4_dist_chunk | server_4
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.chunk;
|
||||
id | hypertable_id | schema_name | table_name | compressed_chunk_id | dropped
|
||||
----+---------------+-----------------------+-----------------------+---------------------+---------
|
||||
3 | 3 | _timescaledb_internal | _hyper_3_3_dist_chunk | | f
|
||||
(1 row)
|
||||
4 | 3 | _timescaledb_internal | _hyper_3_4_dist_chunk | | f
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_server;
|
||||
hypertable_id | server_hypertable_id | server_name | block_chunks
|
||||
@ -440,11 +497,17 @@ SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
chunk_id | server_chunk_id | server_name
|
||||
----------+-----------------+-------------
|
||||
3 | 2 | server_4
|
||||
(1 row)
|
||||
4 | 3 | server_4
|
||||
(2 rows)
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- can't delete b/c it's last data replica
|
||||
SELECT * FROM delete_server('server_4', cascade => true, force => true);
|
||||
ERROR: deleting server "server_4" would mean a data-loss for hypertable "disttable" since server has the only data replica
|
||||
\set ON_ERROR_STOP 1
|
||||
-- Should also clean up hypertable_server when using standard DDL commands
|
||||
DROP SERVER server_4 CASCADE;
|
||||
NOTICE: drop cascades to 2 other objects
|
||||
NOTICE: drop cascades to 3 other objects
|
||||
SELECT * FROM test.show_subtables('disttable');
|
||||
Child | Tablespace
|
||||
-------+------------
|
||||
@ -665,7 +728,7 @@ SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
7 | 2 | server_1
|
||||
(6 rows)
|
||||
|
||||
-- Add additional hypertable
|
||||
-- Add additional hypertable
|
||||
CREATE TABLE disttable_2(time timestamptz, device int, temp float);
|
||||
SELECT * FROM create_distributed_hypertable('disttable_2', 'time', replication_factor => 2, servers => '{"server_1", "server_2", "server_3"}');
|
||||
NOTICE: adding not-null constraint to column "time"
|
||||
@ -712,7 +775,7 @@ SELECT * FROM _timescaledb_catalog.hypertable_server;
|
||||
7 | 2 | server_1 | t
|
||||
(6 rows)
|
||||
|
||||
-- insert more data
|
||||
-- insert more data
|
||||
INSERT INTO disttable VALUES
|
||||
('2019-08-02 10:45', 1, 14.4),
|
||||
('2019-08-15 10:45', 4, 14.9),
|
||||
@ -844,7 +907,7 @@ SELECT * FROM detach_server(NULL, 'disttable');
|
||||
ERROR: invalid server_name: cannot be NULL
|
||||
-- Can't detach server_1 b/c it contains data for disttable
|
||||
SELECT * FROM detach_server('server_1');
|
||||
ERROR: server "server_1" cannot be detached because it contains chunks
|
||||
ERROR: detaching server "server_1" failed because it contains chunks for hypertable "disttable"
|
||||
-- can't detach already detached server
|
||||
SELECT * FROM detach_server('server_2', 'disttable_2');
|
||||
ERROR: server "server_2" is not attached to hypertable "disttable_2"
|
||||
@ -863,6 +926,93 @@ WARNING: new data for hypertable "disttable_2" will be under-replicated due to
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
foreign_table_name | foreign_server_name
|
||||
------------------------+---------------------
|
||||
_hyper_6_10_dist_chunk | server_3
|
||||
_hyper_6_5_dist_chunk | server_1
|
||||
_hyper_6_6_dist_chunk | server_2
|
||||
_hyper_6_7_dist_chunk | server_3
|
||||
_hyper_6_8_dist_chunk | server_3
|
||||
_hyper_6_9_dist_chunk | server_2
|
||||
(6 rows)
|
||||
|
||||
-- force detach server with data
|
||||
SELECT * FROM detach_server('server_3', 'disttable', true);
|
||||
WARNING: hypertable "disttable" has under-replicated chunks due to detaching server "server_3"
|
||||
detach_server
|
||||
---------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- chunk and hypertable metadata should be deleted as well
|
||||
SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
chunk_id | server_chunk_id | server_name
|
||||
----------+-----------------+-------------
|
||||
5 | 1 | server_1
|
||||
5 | 1 | server_2
|
||||
6 | 2 | server_2
|
||||
7 | 2 | server_1
|
||||
8 | 3 | server_2
|
||||
9 | 4 | server_2
|
||||
10 | 5 | server_2
|
||||
(7 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_server;
|
||||
hypertable_id | server_hypertable_id | server_name | block_chunks
|
||||
---------------+----------------------+-------------+--------------
|
||||
6 | 1 | server_1 | f
|
||||
7 | 2 | server_1 | f
|
||||
6 | 1 | server_2 | f
|
||||
(3 rows)
|
||||
|
||||
-- detached server_3 should not show up any more
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
foreign_table_name | foreign_server_name
|
||||
------------------------+---------------------
|
||||
_hyper_6_10_dist_chunk | server_2
|
||||
_hyper_6_5_dist_chunk | server_1
|
||||
_hyper_6_6_dist_chunk | server_2
|
||||
_hyper_6_7_dist_chunk | server_1
|
||||
_hyper_6_8_dist_chunk | server_2
|
||||
_hyper_6_9_dist_chunk | server_2
|
||||
(6 rows)
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- detaching server with last data replica should ERROR even when forcing
|
||||
SELECT * FROM detach_server('server_2', 'disttable', true);
|
||||
ERROR: detaching server "server_2" would mean a data-loss for hypertable "disttable" since server has the only data replica
|
||||
\set ON_ERROR_STOP 1
|
||||
-- drop all chunks
|
||||
SELECT * FROM drop_chunks(table_name => 'disttable', older_than => '2200-01-01 00:00'::timestamptz);
|
||||
drop_chunks
|
||||
----------------------------------------------
|
||||
_timescaledb_internal._hyper_6_5_dist_chunk
|
||||
_timescaledb_internal._hyper_6_6_dist_chunk
|
||||
_timescaledb_internal._hyper_6_7_dist_chunk
|
||||
_timescaledb_internal._hyper_6_8_dist_chunk
|
||||
_timescaledb_internal._hyper_6_9_dist_chunk
|
||||
_timescaledb_internal._hyper_6_10_dist_chunk
|
||||
(6 rows)
|
||||
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
foreign_table_name | foreign_server_name
|
||||
--------------------+---------------------
|
||||
(0 rows)
|
||||
|
||||
SELECT * FROM detach_server('server_2', 'disttable', true);
|
||||
WARNING: new data for hypertable "disttable" will be under-replicated due to detaching server "server_2"
|
||||
detach_server
|
||||
---------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
-- Need explicit password for non-super users to connect
|
||||
ALTER ROLE :ROLE_DEFAULT_CLUSTER_USER CREATEDB PASSWORD 'pass';
|
||||
-- Let's add more servers
|
||||
@ -916,35 +1066,39 @@ NOTICE: skipping hypertable "disttable_3" due to missing permissions
|
||||
|
||||
-- Cleanup
|
||||
RESET ROLE;
|
||||
SELECT * FROM delete_server('server_1', cascade => true);
|
||||
SELECT * FROM delete_server('server_1', cascade => true, force =>true);
|
||||
WARNING: new data for hypertable "disttable" will be under-replicated due to deleting server "server_1"
|
||||
WARNING: new data for hypertable "disttable_2" will be under-replicated due to deleting server "server_1"
|
||||
NOTICE: drop cascades to user mapping for super_user on server server_1
|
||||
delete_server
|
||||
---------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM delete_server('server_2', cascade => true, force =>true);
|
||||
NOTICE: drop cascades to user mapping for super_user on server server_2
|
||||
delete_server
|
||||
---------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM delete_server('server_3', cascade => true, force =>true);
|
||||
NOTICE: drop cascades to user mapping for super_user on server server_3
|
||||
delete_server
|
||||
---------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM delete_server('server_4', cascade => true, force =>true);
|
||||
NOTICE: drop cascades to 2 other objects
|
||||
delete_server
|
||||
---------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM delete_server('server_2', cascade => true);
|
||||
NOTICE: drop cascades to 3 other objects
|
||||
delete_server
|
||||
---------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM delete_server('server_3', cascade => true);
|
||||
NOTICE: drop cascades to 4 other objects
|
||||
delete_server
|
||||
---------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM delete_server('server_4', cascade => true);
|
||||
NOTICE: drop cascades to 2 other objects
|
||||
delete_server
|
||||
---------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM delete_server('server_5', cascade => true);
|
||||
SELECT * FROM delete_server('server_5', cascade => true, force =>true);
|
||||
WARNING: new data for hypertable "disttable_3" will be under-replicated due to deleting server "server_5"
|
||||
WARNING: new data for hypertable "disttable_4" will be under-replicated due to deleting server "server_5"
|
||||
NOTICE: drop cascades to 2 other objects
|
||||
delete_server
|
||||
---------------
|
||||
|
@ -194,22 +194,52 @@ SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
SELECT * FROM drop_chunks(older_than => '2019-05-22 17:18'::timestamptz);
|
||||
|
||||
SELECT * FROM test.show_subtables('disttable');
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
SELECT * FROM _timescaledb_catalog.chunk;
|
||||
SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server('_timescaledb_internal', '_hyper_3_3_dist_chunk', 'server_2');
|
||||
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server('_timescaledb_internal', '_hyper_3_3_dist_chunk', 'server_4');
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- Will fail because server_2 contains chunks
|
||||
SELECT * FROM delete_server('server_2', cascade => true);
|
||||
-- non-existing chunk
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server('x', 'x_chunk', 'server_4');
|
||||
-- non-existing server
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server('_timescaledb_internal', '_hyper_3_3_dist_chunk', 'server_0000');
|
||||
-- NULL try
|
||||
SELECT * FROM _timescaledb_internal.set_chunk_default_server(NULL, NULL, 'server_4');
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
-- Deleting a server removes the "foreign" chunk table(s) that
|
||||
-- reference that server as "primary" and should also remove the
|
||||
-- hypertable_server and chunk_server mappings for that server. In
|
||||
-- the future we might want to fallback to a replica server for those
|
||||
-- chunks that have multiple servers so that the chunk is not removed
|
||||
-- unnecessarily.
|
||||
SELECT * FROM delete_server('server_2', cascade => true);
|
||||
-- unnecessarily. We use force => true b/c server_2 contains chunks.
|
||||
SELECT * FROM delete_server('server_2', cascade => true, force => true);
|
||||
|
||||
SELECT * FROM test.show_subtables('disttable');
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
SELECT * FROM _timescaledb_catalog.chunk;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_server;
|
||||
SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- can't delete b/c it's last data replica
|
||||
SELECT * FROM delete_server('server_4', cascade => true, force => true);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
-- Should also clean up hypertable_server when using standard DDL commands
|
||||
DROP SERVER server_4 CASCADE;
|
||||
|
||||
@ -314,7 +344,7 @@ SELECT * FROM _timescaledb_catalog.chunk;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_server;
|
||||
SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
|
||||
-- Add additional hypertable
|
||||
-- Add additional hypertable
|
||||
CREATE TABLE disttable_2(time timestamptz, device int, temp float);
|
||||
|
||||
SELECT * FROM create_distributed_hypertable('disttable_2', 'time', replication_factor => 2, servers => '{"server_1", "server_2", "server_3"}');
|
||||
@ -331,7 +361,7 @@ SELECT * FROM block_new_chunks_on_server('server_1');
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_server;
|
||||
|
||||
-- insert more data
|
||||
-- insert more data
|
||||
INSERT INTO disttable VALUES
|
||||
('2019-08-02 10:45', 1, 14.4),
|
||||
('2019-08-15 10:45', 4, 14.9),
|
||||
@ -396,6 +426,33 @@ SELECT * FROM detach_server('server_3', 'devices');
|
||||
-- force detach server to become under-replicated for new data
|
||||
SELECT * FROM detach_server('server_3', 'disttable_2', true);
|
||||
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
-- force detach server with data
|
||||
SELECT * FROM detach_server('server_3', 'disttable', true);
|
||||
|
||||
-- chunk and hypertable metadata should be deleted as well
|
||||
SELECT * FROM _timescaledb_catalog.chunk_server;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_server;
|
||||
-- detached server_3 should not show up any more
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
-- detaching server with last data replica should ERROR even when forcing
|
||||
SELECT * FROM detach_server('server_2', 'disttable', true);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
-- drop all chunks
|
||||
SELECT * FROM drop_chunks(table_name => 'disttable', older_than => '2200-01-01 00:00'::timestamptz);
|
||||
SELECT foreign_table_name, foreign_server_name
|
||||
FROM information_schema.foreign_tables
|
||||
ORDER BY foreign_table_name;
|
||||
|
||||
SELECT * FROM detach_server('server_2', 'disttable', true);
|
||||
|
||||
-- Need explicit password for non-super users to connect
|
||||
ALTER ROLE :ROLE_DEFAULT_CLUSTER_USER CREATEDB PASSWORD 'pass';
|
||||
|
||||
@ -425,13 +482,13 @@ SELECT * FROM detach_server('server_4');
|
||||
|
||||
-- Cleanup
|
||||
RESET ROLE;
|
||||
SELECT * FROM delete_server('server_1', cascade => true);
|
||||
SELECT * FROM delete_server('server_2', cascade => true);
|
||||
SELECT * FROM delete_server('server_3', cascade => true);
|
||||
SELECT * FROM delete_server('server_4', cascade => true);
|
||||
SELECT * FROM delete_server('server_5', cascade => true);
|
||||
SELECT * FROM delete_server('server_1', cascade => true, force =>true);
|
||||
SELECT * FROM delete_server('server_2', cascade => true, force =>true);
|
||||
SELECT * FROM delete_server('server_3', cascade => true, force =>true);
|
||||
SELECT * FROM delete_server('server_4', cascade => true, force =>true);
|
||||
SELECT * FROM delete_server('server_5', cascade => true, force =>true);
|
||||
DROP DATABASE server_1;
|
||||
DROP DATABASE server_2;
|
||||
DROP DATABASE server_3;
|
||||
DROP DATABASE server_4;
|
||||
DROP DATABASE server_5;
|
||||
DROP DATABASE server_5;
|
||||
|
Loading…
x
Reference in New Issue
Block a user