Use cached Chunk struct when considering compressed paths

Full catalog lookups for a Chunk are expensive, avoiding them speeds up
the planning.
This commit is contained in:
Alexander Kuzmenkov 2023-08-10 15:32:40 +02:00
parent fb617e4150
commit 04ce1bc498
6 changed files with 40 additions and 19 deletions

View File

@ -1507,7 +1507,7 @@ ts_plan_expand_hypertable_chunks(Hypertable *ht, PlannerInfo *root, RelOptInfo *
#endif #endif
} }
ts_get_private_reloptinfo(child_rel)->chunk = chunks[i]; ts_get_private_reloptinfo(child_rel)->cached_chunk_struct = chunks[i];
Assert(chunks[i]->table_id == root->simple_rte_array[child_rtindex]->relid); Assert(chunks[i]->table_id == root->simple_rte_array[child_rtindex]->relid);
} }
} }

View File

@ -1096,18 +1096,17 @@ apply_optimizations(PlannerInfo *root, TsRelType reltype, RelOptInfo *rel, Range
case TS_REL_CHUNK_STANDALONE: case TS_REL_CHUNK_STANDALONE:
case TS_REL_CHUNK_CHILD: case TS_REL_CHUNK_CHILD:
ts_sort_transform_optimization(root, rel); ts_sort_transform_optimization(root, rel);
/*
* Since the sort optimization adds new paths to the rel it has
* to happen before any optimizations that replace pathlist.
*/
if (ts_cm_functions->set_rel_pathlist_query != NULL)
ts_cm_functions->set_rel_pathlist_query(root, rel, rel->relid, rte, ht);
break; break;
default: default:
break; break;
} }
/*
* Since the sort optimization adds new paths to the rel it has
* to happen before any optimizations that replace pathlist.
*/
if (ts_cm_functions->set_rel_pathlist_query != NULL)
ts_cm_functions->set_rel_pathlist_query(root, rel, rel->relid, rte, ht);
if (reltype == TS_REL_HYPERTABLE && if (reltype == TS_REL_HYPERTABLE &&
#if PG14_GE #if PG14_GE
(root->parse->commandType == CMD_SELECT || root->parse->commandType == CMD_DELETE || (root->parse->commandType == CMD_SELECT || root->parse->commandType == CMD_DELETE ||

View File

@ -40,7 +40,7 @@ typedef struct TimescaleDBPrivate
TsFdwRelInfo *fdw_relation_info; TsFdwRelInfo *fdw_relation_info;
/* Cached chunk data for the chunk relinfo. */ /* Cached chunk data for the chunk relinfo. */
Chunk *chunk; Chunk *cached_chunk_struct;
} TimescaleDBPrivate; } TimescaleDBPrivate;
extern TSDLLEXPORT bool ts_rte_is_hypertable(const RangeTblEntry *rte, bool *isdistributed); extern TSDLLEXPORT bool ts_rte_is_hypertable(const RangeTblEntry *rte, bool *isdistributed);

View File

@ -78,7 +78,7 @@ data_node_chunk_assignment_assign_chunk(DataNodeChunkAssignments *scas, RelOptIn
*/ */
Oid remote_chunk_relid = InvalidOid; Oid remote_chunk_relid = InvalidOid;
ListCell *lc; ListCell *lc;
foreach (lc, chunk_private->chunk->data_nodes) foreach (lc, chunk_private->cached_chunk_struct->data_nodes)
{ {
ChunkDataNode *cdn = (ChunkDataNode *) lfirst(lc); ChunkDataNode *cdn = (ChunkDataNode *) lfirst(lc);
if (cdn->foreign_server_oid == chunkrel->serverid) if (cdn->foreign_server_oid == chunkrel->serverid)
@ -94,7 +94,7 @@ data_node_chunk_assignment_assign_chunk(DataNodeChunkAssignments *scas, RelOptIn
*/ */
old = MemoryContextSwitchTo(scas->mctx); old = MemoryContextSwitchTo(scas->mctx);
sca->chunk_relids = bms_add_member(sca->chunk_relids, chunkrel->relid); sca->chunk_relids = bms_add_member(sca->chunk_relids, chunkrel->relid);
sca->chunks = lappend(sca->chunks, chunk_private->chunk); sca->chunks = lappend(sca->chunks, chunk_private->cached_chunk_struct);
sca->remote_chunk_ids = lappend_int(sca->remote_chunk_ids, remote_chunk_relid); sca->remote_chunk_ids = lappend_int(sca->remote_chunk_ids, remote_chunk_relid);
sca->pages += chunkrel->pages; sca->pages += chunkrel->pages;
sca->rows += chunkrel->rows; sca->rows += chunkrel->rows;

View File

@ -300,10 +300,10 @@ estimate_chunk_size(PlannerInfo *root, RelOptInfo *chunk_rel)
* exclusion, so we'll have to look up this info now. * exclusion, so we'll have to look up this info now.
*/ */
TimescaleDBPrivate *chunk_private = ts_get_private_reloptinfo(chunk_rel); TimescaleDBPrivate *chunk_private = ts_get_private_reloptinfo(chunk_rel);
if (chunk_private->chunk == NULL) if (chunk_private->cached_chunk_struct == NULL)
{ {
RangeTblEntry *chunk_rte = planner_rt_fetch(chunk_rel->relid, root); RangeTblEntry *chunk_rte = planner_rt_fetch(chunk_rel->relid, root);
chunk_private->chunk = chunk_private->cached_chunk_struct =
ts_chunk_get_by_relid(chunk_rte->relid, true /* fail_if_not_found */); ts_chunk_get_by_relid(chunk_rte->relid, true /* fail_if_not_found */);
} }
@ -319,7 +319,8 @@ estimate_chunk_size(PlannerInfo *root, RelOptInfo *chunk_rel)
Hypertable *ht = ts_hypertable_cache_get_entry(hcache, parent_rte->relid, CACHE_FLAG_NONE); Hypertable *ht = ts_hypertable_cache_get_entry(hcache, parent_rte->relid, CACHE_FLAG_NONE);
Hyperspace *hyperspace = ht->space; Hyperspace *hyperspace = ht->space;
const double fillfactor = estimate_chunk_fillfactor(chunk_private->chunk, hyperspace); const double fillfactor =
estimate_chunk_fillfactor(chunk_private->cached_chunk_struct, hyperspace);
/* Can't have nonzero tuples in zero pages */ /* Can't have nonzero tuples in zero pages */
Assert(parent_private->average_chunk_pages != 0 || parent_private->average_chunk_tuples <= 0); Assert(parent_private->average_chunk_pages != 0 || parent_private->average_chunk_tuples <= 0);

View File

@ -121,16 +121,37 @@ tsl_set_rel_pathlist_query(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeT
* We check if it is the ONLY case by calling ts_rte_is_marked_for_expansion. * We check if it is the ONLY case by calling ts_rte_is_marked_for_expansion.
* Respecting ONLY here is important to not break postgres tools like pg_dump. * Respecting ONLY here is important to not break postgres tools like pg_dump.
*/ */
TimescaleDBPrivate *fdw_private = (TimescaleDBPrivate *) rel->fdw_private;
if (ts_guc_enable_transparent_decompression && ht && if (ts_guc_enable_transparent_decompression && ht &&
(rel->reloptkind == RELOPT_OTHER_MEMBER_REL || (rel->reloptkind == RELOPT_OTHER_MEMBER_REL ||
(rel->reloptkind == RELOPT_BASEREL && ts_rte_is_marked_for_expansion(rte))) && (rel->reloptkind == RELOPT_BASEREL && ts_rte_is_marked_for_expansion(rte))) &&
TS_HYPERTABLE_HAS_COMPRESSION_TABLE(ht) && rel->fdw_private != NULL && TS_HYPERTABLE_HAS_COMPRESSION_TABLE(ht) && fdw_private != NULL && fdw_private->compressed)
((TimescaleDBPrivate *) rel->fdw_private)->compressed)
{ {
Chunk *chunk = ts_chunk_get_by_relid(rte->relid, true); if (fdw_private->cached_chunk_struct == NULL)
{
/*
* We can not have the cached Chunk struct,
* 1) if it was a direct query on the chunk;
* 2) if it is not a SELECT QUERY.
* Caching is done by our hypertable expansion, which doesn't run in
* these cases.
*
* Also on PG13 when a DELETE query runs through SPI, its command
* type is CMD_SELECT. Apparently it goes into inheritance_planner,
* which uses a hack to pretend it's actually a SELECT query, but
* for some reason for non-SPI queries the query type is still
* correct. You can observe it in the continuous_aggs-13 test.
* Just ignore this assertion on 13 and look up the chunk.
*/
#if PG14_GE
Assert(rel->reloptkind == RELOPT_BASEREL || root->parse->commandType != CMD_SELECT);
#endif
fdw_private->cached_chunk_struct =
ts_chunk_get_by_relid(rte->relid, /* fail_if_not_found = */ true);
}
if (chunk->fd.compressed_chunk_id != INVALID_CHUNK_ID) if (fdw_private->cached_chunk_struct->fd.compressed_chunk_id != INVALID_CHUNK_ID)
ts_decompress_chunk_generate_paths(root, rel, ht, chunk); ts_decompress_chunk_generate_paths(root, rel, ht, fdw_private->cached_chunk_struct);
} }
} }