Skip to content

Commit

Permalink
Optimize planning times when hypertables have many chunks
Browse files Browse the repository at this point in the history
This planner optimization reduces planning times when a hypertable has many chunks.
It does this by expanding hypertable chunks manually, eliding the `expand_inherited_tables`
logic used by PG.

Slow planning time were previously seen because `expand_inherited_tables` expands all chunks of
a hypertable, without regard to constraints present in the query. Then, `get_relation_info` is
the called on all chunks before constraint exclusion. Getting the statistics an many chunks ends
up being expensive because RelationGetNumberOfBlocks has to open the file for each relation.
This gets even worse under high concurrency.

This logic solves this by expanding only the chunks needed to fulfil the query instead of all chunks.
In effect, it moves chunk exclusion up in the planning process. But, we actually don't use constraint
exclusion here, but rather a variant of range exclusion implemented
by HypertableRestrictInfo.
  • Loading branch information
cevian committed Apr 30, 2018
1 parent 43f812c commit f0e23c2
Show file tree
Hide file tree
Showing 30 changed files with 1,945 additions and 399 deletions.
9 changes: 6 additions & 3 deletions src/CMakeLists.txt
Expand Up @@ -56,10 +56,12 @@ set(HEADERS
hypertable_cache.h
hypertable.h
hypertable_insert.h
hypertable_restrict_info.h
indexing.h
parse_rewrite.h
partitioning.h
planner_utils.h
planner_import.h
plan_expand_hypertable.h
process_utility.h
scanner.h
subspace_store.h
Expand Down Expand Up @@ -93,13 +95,14 @@ set(SOURCES
hypertable.c
hypertable_cache.c
hypertable_insert.c
hypertable_restrict_info.c
indexing.c
init.c
parse_analyze.c
parse_rewrite.c
partitioning.c
planner.c
planner_import.c
planner_utils.c
plan_expand_hypertable.c
process_utility.c
scanner.c
sort_transform.c
Expand Down
60 changes: 58 additions & 2 deletions src/chunk.c
Expand Up @@ -762,7 +762,7 @@ chunk_is_complete(ChunkScanCtx *scanctx, Chunk *chunk)
if (scanctx->space->num_dimensions != chunk->constraints->num_dimension_constraints)
return false;

scanctx->data = chunk;
scanctx->data = lappend(scanctx->data, chunk);
return true;
}

Expand All @@ -772,10 +772,21 @@ chunk_is_complete(ChunkScanCtx *scanctx, Chunk *chunk)
static Chunk *
chunk_scan_ctx_get_chunk(ChunkScanCtx *ctx)
{
ctx->data = NULL;
ctx->data = NIL;

chunk_scan_ctx_foreach_chunk(ctx, chunk_is_complete, 1);

return (ctx->data == NIL ? NULL : linitial(ctx->data));
}

/* Finds all chunks that have a complete set of constraints. */
static List *
chunk_scan_ctx_get_chunk_list(ChunkScanCtx *ctx)
{
ctx->data = NIL;

chunk_scan_ctx_foreach_chunk(ctx, chunk_is_complete, 0);

return ctx->data;
}

Expand Down Expand Up @@ -837,6 +848,51 @@ chunk_find(Hyperspace *hs, Point *p)
return chunk;
}

List *
chunk_find_all_oids(Hyperspace *hs, List *dimension_vecs, LOCKMODE lockmode)
{
List *chunk_list,
*oid_list = NIL;
ChunkScanCtx ctx;
ListCell *lc;

/* The scan context will keep the state accumulated during the scan */
chunk_scan_ctx_init(&ctx, hs, NULL);

/* Do not abort the scan when one chunk is found */
ctx.early_abort = false;

/* Scan all dimensions for slices enclosing the point */
foreach(lc, dimension_vecs)
{
DimensionVec *vec = lfirst(lc);

dimension_slice_and_chunk_constraint_join(&ctx, vec);
}

/* Get a list of chunks that each have N matching dimension constraints */
chunk_list = chunk_scan_ctx_get_chunk_list(&ctx);

chunk_scan_ctx_destroy(&ctx);

foreach(lc, chunk_list)
{
Chunk *chunk = lfirst(lc);

/* Fill in the rest of the chunk's data from the chunk table */
chunk_fill_stub(chunk, false);

/* chunk constraints left unfilled */

if (lockmode != NoLock)
LockRelationOid(chunk->table_id, lockmode);

oid_list = lappend_oid(oid_list, chunk->table_id);
}

return oid_list;
}

Chunk *
chunk_copy(Chunk *chunk)
{
Expand Down
1 change: 1 addition & 0 deletions src/chunk.h
Expand Up @@ -66,6 +66,7 @@ extern Chunk *chunk_create(Hypertable *ht, Point *p, const char *schema, const c
extern Chunk *chunk_create_stub(int32 id, int16 num_constraints);
extern void chunk_free(Chunk *chunk);
extern Chunk *chunk_find(Hyperspace *hs, Point *p);
extern List *chunk_find_all_oids(Hyperspace *hs, List *dimension_vecs, LOCKMODE lockmode);
extern Chunk *chunk_copy(Chunk *chunk);
extern Chunk *chunk_get_by_name(const char *schema_name, const char *table_name, int16 num_constraints, bool fail_if_not_found);
extern Chunk *chunk_get_by_relid(Oid relid, int16 num_constraints, bool fail_if_not_found);
Expand Down
11 changes: 7 additions & 4 deletions src/constraint_aware_append.c
Expand Up @@ -420,11 +420,14 @@ constraint_aware_append_path_create(PlannerInfo *root, Hypertable *ht, Path *sub
break;
}

appinfo = linitial(root->append_rel_list);
relid = root->simple_rte_array[appinfo->child_relid]->relid;
if (list_length(root->append_rel_list) > 1)
{
appinfo = linitial(root->append_rel_list);
relid = root->simple_rte_array[appinfo->child_relid]->relid;

if (relid == ht->main_table_relid)
root->append_rel_list = list_delete_first(root->append_rel_list);
if (relid == ht->main_table_relid)
root->append_rel_list = list_delete_first(root->append_rel_list);
}

return &path->cpath.path;
}
83 changes: 83 additions & 0 deletions src/dimension_slice.c
Expand Up @@ -6,6 +6,9 @@
#include <utils/rel.h>
#include <catalog/indexing.h>
#include <funcapi.h>
#include <utils/lsyscache.h>
#include <catalog/pg_opfamily.h>
#include <catalog/pg_type.h>

#include "catalog.h"
#include "dimension_slice.h"
Expand Down Expand Up @@ -166,6 +169,84 @@ dimension_slice_scan_limit(int32 dimension_id, int64 coordinate, int limit)
return dimension_vec_sort(&slices);
}

/*
* Look for all ranges where value > lower_bound and value < upper_bound
*
*/
DimensionVec *
dimension_slice_scan_range_limit(int32 dimension_id, StrategyNumber start_strategy, int64 start_value, StrategyNumber end_strategy, int64 end_value, int limit)
{
ScanKeyData scankey[3];
DimensionVec *slices = dimension_vec_create(limit > 0 ? limit : DIMENSION_VEC_DEFAULT_SIZE);
int nkeys = 1;

/*
* Perform an index scan for slices matching the dimension's ID and which
* enclose the coordinate.
*/
ScanKeyInit(&scankey[0], Anum_dimension_slice_dimension_id_range_start_range_end_idx_dimension_id,
BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(dimension_id));
if (start_strategy != InvalidStrategy)
{
Oid opno = get_opfamily_member(INTEGER_BTREE_FAM_OID, INT8OID, INT8OID, start_strategy);
Oid proc = get_opcode(opno);

Assert(OidIsValid(proc));

ScanKeyInit(&scankey[nkeys++],
Anum_dimension_slice_dimension_id_range_start_range_end_idx_range_start,
start_strategy,
proc,
Int64GetDatum(start_value));
}
if (end_strategy != InvalidStrategy)
{
Oid opno = get_opfamily_member(INTEGER_BTREE_FAM_OID, INT8OID, INT8OID, end_strategy);
Oid proc = get_opcode(opno);

Assert(OidIsValid(proc));

/*
* range_end is stored as exclusive, so add 1 to the value being
* searched. Also avoid overflow
*/
if (end_value != PG_INT64_MAX)
{
end_value++;

/*
* If getting as input INT64_MAX-1, need to remap the incremented
* value back to INT64_MAX-1
*/
end_value = REMAP_LAST_COORDINATE(end_value);
}
else
{
/*
* The point with INT64_MAX gets mapped to INT64_MAX-1 so
* incrementing that gets you to INT_64MAX
*/
end_value = PG_INT64_MAX;
}

ScanKeyInit(&scankey[nkeys++],
Anum_dimension_slice_dimension_id_range_start_range_end_idx_range_end,
end_strategy,
proc,
Int64GetDatum(end_value));
}

dimension_slice_scan_limit_internal(DIMENSION_SLICE_DIMENSION_ID_RANGE_START_RANGE_END_IDX,
scankey,
nkeys,
dimension_vec_tuple_found,
&slices,
limit,
AccessShareLock);

return dimension_vec_sort(&slices);
}

/*
* Scan for slices that collide/overlap with the given range.
*
Expand Down Expand Up @@ -408,13 +489,15 @@ dimension_slice_cut(DimensionSlice *to_cut, DimensionSlice *other, int64 coord)
{
/* Cut "before" the coordinate */
to_cut->fd.range_start = other->fd.range_end;

return true;
}
else if (other->fd.range_start > coord &&
other->fd.range_start < to_cut->fd.range_end)
{
/* Cut "after" the coordinate */
to_cut->fd.range_end = other->fd.range_start;

return true;
}

Expand Down
1 change: 1 addition & 0 deletions src/dimension_slice.h
Expand Up @@ -25,6 +25,7 @@ typedef struct DimensionVec DimensionVec;
typedef struct Hypercube Hypercube;

extern DimensionVec *dimension_slice_scan_limit(int32 dimension_id, int64 coordinate, int limit);
extern DimensionVec *dimension_slice_scan_range_limit(int32 dimension_id, StrategyNumber start_strategy, int64 start_value, StrategyNumber end_strategy, int64 end_value, int limit);
extern DimensionVec *dimension_slice_collision_scan_limit(int32 dimension_id, int64 range_start, int64 range_end, int limit);
extern Hypercube *dimension_slice_point_scan(Hyperspace *space, int64 point[]);
extern DimensionSlice *dimension_slice_scan_for_existing(DimensionSlice *slice);
Expand Down

0 comments on commit f0e23c2

Please sign in to comment.