Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 192e856

Browse files
committed
Implement autopartitioning of tablespaces
1 parent 105eb36 commit 192e856

File tree

7 files changed

+268
-3
lines changed

7 files changed

+268
-3
lines changed

src/backend/access/common/reloptions.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,15 @@ static relopt_bool boolRelOpts[] =
164164

165165
static relopt_int intRelOpts[] =
166166
{
167+
{
168+
{
169+
"max_files",
170+
"Limit number of files in tablespace",
171+
RELOPT_KIND_TABLESPACE,
172+
AccessExclusiveLock
173+
},
174+
0, 0, 10000 /* by default: no limit, restrict max value to 10k otherwise counting files in tablespace on each DDL will become too expensive */
175+
},
167176
{
168177
{
169178
"fillfactor",
@@ -1690,7 +1699,8 @@ tablespace_reloptions(Datum reloptions, bool validate)
16901699
static const relopt_parse_elt tab[] = {
16911700
{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
16921701
{"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
1693-
{"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)}
1702+
{"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)},
1703+
{"max_files", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, max_files)}
16941704
};
16951705

16961706
return (bytea *) build_reloptions(reloptions, validate,

src/backend/commands/indexcmds.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,8 @@ DefineIndex(Oid relationId,
666666
if (aclresult != ACLCHECK_OK)
667667
aclcheck_error(aclresult, OBJECT_TABLESPACE,
668668
get_tablespace_name(tablespaceId));
669+
670+
tablespaceId = GetCurrentTablespaceSegment(tablespaceId);
669671
}
670672

671673
/*

src/backend/commands/tablecmds.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
528528
static List *GetParentedForeignKeyRefs(Relation partition);
529529
static void ATDetachCheckNoForeignKeyRefs(Relation partition);
530530

531-
532531
/* ----------------------------------------------------------------
533532
* DefineRelation
534533
* Creates a new relation.
@@ -699,8 +698,9 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
699698
if (aclresult != ACLCHECK_OK)
700699
aclcheck_error(aclresult, OBJECT_TABLESPACE,
701700
get_tablespace_name(tablespaceId));
702-
}
703701

702+
tablespaceId = GetCurrentTablespaceSegment(tablespaceId);
703+
}
704704
/* In all cases disallow placing user relations in pg_global */
705705
if (tablespaceId == GLOBALTABLESPACE_OID)
706706
ereport(ERROR,

src/backend/commands/tablespace.c

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
#include "utils/lsyscache.h"
8383
#include "utils/memutils.h"
8484
#include "utils/rel.h"
85+
#include "utils/spccache.h"
8586
#include "utils/varlena.h"
8687

8788
/* GUC variables */
@@ -222,6 +223,54 @@ TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
222223
pfree(dir);
223224
}
224225

226+
static int
227+
getIntOption(List *options, char const *name, int defaultValue)
228+
{
229+
ListCell *cell;
230+
231+
foreach(cell, options)
232+
{
233+
DefElem *def = (DefElem *) lfirst(cell);
234+
if (strcmp(def->defname, name) == 0)
235+
{
236+
if (def->arg != NULL)
237+
{
238+
switch (nodeTag(def->arg))
239+
{
240+
case T_Integer:
241+
return (int32) intVal(def->arg);
242+
case T_String:
243+
return atoi(strVal(def->arg));
244+
default:
245+
ereport(ERROR,
246+
(errcode(ERRCODE_SYNTAX_ERROR),
247+
errmsg("%s requires an integer value",
248+
name)));
249+
}
250+
break;
251+
}
252+
}
253+
}
254+
return defaultValue;
255+
}
256+
257+
static void
258+
AtomicChangeLink(char const* link_path, char const* new_location)
259+
{
260+
char* tmp_link_path = psprintf("%s.tmp", link_path);
261+
(void)unlink(tmp_link_path); /* remove temporary link if exists */
262+
if (symlink(new_location, tmp_link_path) < 0)
263+
ereport(ERROR,
264+
(errcode_for_file_access(),
265+
errmsg("could not create symbolic link \"%s\": %m",
266+
tmp_link_path)));
267+
if (rename(tmp_link_path, link_path) < 0)
268+
ereport(ERROR,
269+
(errcode_for_file_access(),
270+
errmsg("could not rename symbolic link \"%s\": %m",
271+
link_path)));
272+
}
273+
225274
/*
226275
* Create a table space
227276
*
@@ -366,6 +415,30 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
366415
/* Post creation hook for new tablespace */
367416
InvokeObjectPostCreateHook(TableSpaceRelationId, tablespaceoid, 0);
368417

418+
if (getIntOption(stmt->options, "max_files", 0) != 0)
419+
{
420+
/* Limited tablespace */
421+
char* segment = psprintf("%s/%u", location, tablespaceoid);
422+
char* link_path = psprintf("%s/current_segment", location);
423+
424+
pfree(location);
425+
location = segment;
426+
if (MakePGDirectory(location) < 0)
427+
{
428+
if (errno == EEXIST)
429+
ereport(ERROR,
430+
(errcode(ERRCODE_OBJECT_IN_USE),
431+
errmsg("directory \"%s\" already in use as a tablespace",
432+
location)));
433+
else
434+
ereport(ERROR,
435+
(errcode_for_file_access(),
436+
errmsg("could not create directory \"%s\": %m",
437+
location)));
438+
}
439+
AtomicChangeLink(link_path, location);
440+
}
441+
369442
create_tablespace_directories(location, tablespaceoid);
370443

371444
/* Record the filesystem change in XLOG */
@@ -1554,3 +1627,161 @@ tblspc_redo(XLogReaderState *record)
15541627
else
15551628
elog(PANIC, "tblspc_redo: unknown op code %u", info);
15561629
}
1630+
1631+
1632+
static int
1633+
RecursiveCountFiles(char const* linkloc_with_version_dir, int level)
1634+
{
1635+
char *subfile;
1636+
int count = 0;
1637+
DIR *dirdesc = AllocateDir(linkloc_with_version_dir);
1638+
struct dirent *de;
1639+
1640+
if (dirdesc == NULL)
1641+
{
1642+
return 0;
1643+
}
1644+
while ((de = ReadDir(dirdesc, linkloc_with_version_dir)) != NULL)
1645+
{
1646+
if (strcmp(de->d_name, ".") == 0 ||
1647+
strcmp(de->d_name, "..") == 0)
1648+
continue;
1649+
1650+
subfile = psprintf("%s/%s", linkloc_with_version_dir, de->d_name);
1651+
/* Assume that relations files are always at second level */
1652+
count += level > 0 ? RecursiveCountFiles(subfile, level - 1) : 1;
1653+
}
1654+
FreeDir(dirdesc);
1655+
return count;
1656+
}
1657+
1658+
static int
1659+
CountTablespaceFiles(char const* curr_segment)
1660+
{
1661+
char* linkloc_with_version_dir = psprintf("%s/%s", curr_segment, TABLESPACE_VERSION_DIRECTORY);
1662+
return RecursiveCountFiles(linkloc_with_version_dir, 1);
1663+
}
1664+
1665+
static Oid
1666+
GetCurrentTablespaceSegmentId(char* segment_path)
1667+
{
1668+
char* tblspc = strrchr(segment_path, '/');
1669+
if (tblspc == NULL)
1670+
ereport(ERROR,
1671+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1672+
errmsg("Invalid current tablespace segment \"%s\"",
1673+
segment_path)));
1674+
*tblspc++ = '\0';
1675+
return atoi(tblspc);
1676+
}
1677+
1678+
static char*
1679+
GetCurrentTablespaceSegmentLocation(Oid tablespaceId)
1680+
{
1681+
char link_target[MAXPGPATH];
1682+
char* link_path = psprintf("pg_tblspc/%u", tablespaceId);
1683+
int len = readlink(link_path, link_target, sizeof(link_target));
1684+
if (len < 0)
1685+
ereport(ERROR,
1686+
(errcode_for_file_access(),
1687+
errmsg("could not read symbolic link \"%s\": %m",
1688+
link_path)));
1689+
link_target[len] = '\0';
1690+
pfree(link_path);
1691+
GetCurrentTablespaceSegmentId(link_target);
1692+
link_path = psprintf("%s/current_segment", link_target);
1693+
len = readlink(link_path, link_target, sizeof(link_target));
1694+
if (len < 0)
1695+
ereport(ERROR,
1696+
(errcode_for_file_access(),
1697+
errmsg("could not read symbolic link \"%s\": %m",
1698+
link_path)));
1699+
link_target[len] = '\0';
1700+
pfree(link_path);
1701+
return pstrdup(link_target);
1702+
}
1703+
1704+
Oid
1705+
GetCurrentTablespaceSegment(Oid tablespaceId)
1706+
{
1707+
CreateTableSpaceStmt* stmt;
1708+
Relation rel;
1709+
ScanKeyData entry[1];
1710+
TableScanDesc scan;
1711+
HeapTuple tup;
1712+
Oid spcowner;
1713+
bool isnull;
1714+
List* relopt;
1715+
int num_files;
1716+
char* orig_spcname;
1717+
char* new_spcname;
1718+
char* curr_segment;
1719+
bool segment_is_used;
1720+
int limit;
1721+
Oid segmentId;
1722+
1723+
/* If there is limit for maximal files in tablespace, then count files in the current segment and create new one if needed */
1724+
limit = get_tablespace_max_files(tablespaceId);
1725+
if (limit == 0)
1726+
return tablespaceId;
1727+
1728+
curr_segment = GetCurrentTablespaceSegmentLocation(tablespaceId);
1729+
num_files = CountTablespaceFiles(curr_segment);
1730+
segmentId = GetCurrentTablespaceSegmentId(curr_segment);
1731+
1732+
if (num_files >= limit) /* Check for tablespace overflow */
1733+
{
1734+
/* Search pg_tablespace */
1735+
rel = table_open(TableSpaceRelationId, RowExclusiveLock);
1736+
1737+
/* Extract information about original tablespace */
1738+
ScanKeyInit(&entry[0],
1739+
Anum_pg_tablespace_oid,
1740+
BTEqualStrategyNumber, F_OIDEQ,
1741+
ObjectIdGetDatum(tablespaceId));
1742+
scan = table_beginscan_catalog(rel, 1, entry);
1743+
tup = heap_getnext(scan, ForwardScanDirection);
1744+
if (!HeapTupleIsValid(tup)) /* If tablespace with specified OID was not found, then most likely it was removed */
1745+
ereport(ERROR,
1746+
(errcode(ERRCODE_UNDEFINED_OBJECT),
1747+
errmsg("tablespace with OID %d was removed",
1748+
tablespaceId)));
1749+
/* Extract tablespace options */
1750+
relopt = untransformRelOptions(heap_getattr(tup, Anum_pg_tablespace_spcoptions,
1751+
RelationGetDescr(rel), &isnull));
1752+
/* Original tablespace name */
1753+
orig_spcname = pstrdup(NameStr(((Form_pg_tablespace) GETSTRUCT(tup))->spcname));
1754+
spcowner = ((Form_pg_tablespace) GETSTRUCT(tup))->spcowner;
1755+
1756+
table_endscan(scan);
1757+
1758+
/* Choose new unused tablespace name by concatanating segno suffix */
1759+
do
1760+
{
1761+
new_spcname = psprintf("%s.%d", orig_spcname, segmentId++);
1762+
ScanKeyInit(&entry[0],
1763+
Anum_pg_tablespace_spcname,
1764+
BTEqualStrategyNumber, F_NAMEEQ,
1765+
CStringGetDatum(new_spcname));
1766+
scan = table_beginscan_catalog(rel, 1, entry);
1767+
tup = heap_getnext(scan, ForwardScanDirection);
1768+
segment_is_used = HeapTupleIsValid(tup);
1769+
table_endscan(scan);
1770+
} while (segment_is_used);
1771+
1772+
table_close(rel, RowExclusiveLock);
1773+
1774+
stmt = makeNode(CreateTableSpaceStmt);
1775+
stmt->tablespacename = new_spcname;
1776+
stmt->owner = makeNode(RoleSpec);
1777+
stmt->owner->roletype = ROLESPEC_CSTRING;
1778+
stmt->owner->rolename = GetUserNameFromId(spcowner, false);
1779+
stmt->location = curr_segment;
1780+
stmt->options = relopt;
1781+
1782+
/* Create new tablespace */
1783+
segmentId = CreateTableSpace(stmt);
1784+
Assert(OidIsValid(segmentId));
1785+
}
1786+
return segmentId;
1787+
}

src/backend/utils/cache/spccache.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,21 @@ get_tablespace_io_concurrency(Oid spcid)
221221
else
222222
return spc->opts->effective_io_concurrency;
223223
}
224+
225+
/*
226+
* get_tablespace_max_files
227+
*
228+
* This value is not locked by the transaction, so this value may
229+
* be changed while a SELECT that has used these values for planning
230+
* is still executing.
231+
*/
232+
int
233+
get_tablespace_max_files(Oid spcid)
234+
{
235+
TableSpaceCacheEntry *spc = get_tablespace(spcid);
236+
237+
if (!spc->opts || spc->opts->max_files <= 0)
238+
return 0; /* no limit by default */
239+
else
240+
return spc->opts->max_files;
241+
}

src/include/commands/tablespace.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ typedef struct TableSpaceOpts
4040
float8 random_page_cost;
4141
float8 seq_page_cost;
4242
int effective_io_concurrency;
43+
int max_files;
4344
} TableSpaceOpts;
4445

4546
extern Oid CreateTableSpace(CreateTableSpaceStmt *stmt);
@@ -53,6 +54,8 @@ extern Oid GetDefaultTablespace(char relpersistence, bool partitioned);
5354

5455
extern void PrepareTempTablespaces(void);
5556

57+
extern Oid GetCurrentTablespaceSegment(Oid tablespaceId);
58+
5659
extern Oid get_tablespace_oid(const char *tablespacename, bool missing_ok);
5760
extern char *get_tablespace_name(Oid spc_oid);
5861

src/include/utils/spccache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
void get_tablespace_page_costs(Oid spcid, float8 *spc_random_page_cost,
1717
float8 *spc_seq_page_cost);
1818
int get_tablespace_io_concurrency(Oid spcid);
19+
int get_tablespace_max_files(Oid spcid);
1920

2021
#endif /* SPCCACHE_H */

0 commit comments

Comments
 (0)