diff --git a/src/postgres/src/backend/utils/cache/catcache.c b/src/postgres/src/backend/utils/cache/catcache.c index 9dfe7931f59a..efdc77fa7382 100644 --- a/src/postgres/src/backend/utils/cache/catcache.c +++ b/src/postgres/src/backend/utils/cache/catcache.c @@ -83,7 +83,8 @@ /* Cache management header --- pointer is NULL until created */ static CatCacheHeader *CacheHdr = NULL; -static long NumCatalogCacheMisses; +static long YbNumCatalogCacheMisses; +static long YbNumCatalogCacheIdMisses[SysCacheSize] = {0}; static inline HeapTuple SearchCatCacheInternal(CatCache *cache, int nkeys, @@ -1821,7 +1822,10 @@ SearchCatCacheMiss(CatCache *cache, relation = heap_open(cache->cc_reloid, AccessShareLock); if (IsYugaByteEnabled()) - NumCatalogCacheMisses++; + { + YbNumCatalogCacheMisses++; + YbNumCatalogCacheIdMisses[cache->id]++; + } if (yb_debug_log_catcache_events) { @@ -2671,9 +2675,15 @@ PrintCatCacheListLeakWarning(CatCList *list) } long -GetCatCacheMisses() +YbGetCatCacheMisses() +{ + return YbNumCatalogCacheMisses; +} + +long* +YbGetCatCacheIdMisses() { - return NumCatalogCacheMisses; + return YbNumCatalogCacheIdMisses; } YbCatCListIterator diff --git a/src/postgres/src/backend/utils/cache/relcache.c b/src/postgres/src/backend/utils/cache/relcache.c index 072651bdfc0f..ea4eb8f9b749 100644 --- a/src/postgres/src/backend/utils/cache/relcache.c +++ b/src/postgres/src/backend/utils/cache/relcache.c @@ -5750,6 +5750,7 @@ RelationCacheInitializePhase3(void) YbPrefetchRequiredData(preload_rel_cache); Assert(YBCIsSysTablePrefetchingStarted()); + Assert(YbCheckCatalogCacheIndexNameTable()); } /* diff --git a/src/postgres/src/backend/utils/cache/syscache.c b/src/postgres/src/backend/utils/cache/syscache.c index 71bc258240dc..91d40bc1b930 100644 --- a/src/postgres/src/backend/utils/cache/syscache.c +++ b/src/postgres/src/backend/utils/cache/syscache.c @@ -20,6 +20,8 @@ */ #include "postgres.h" +#include + #include "access/genam.h" #include "access/heapam.h" #include "access/htup_details.h" @@ -1003,6 +1005,90 @@ static const struct cachedesc cacheinfo[] = { } }; +static const char *yb_cache_index_name_table[] = { + "pg_aggregate_fnoid_index", + "pg_am_name_index", + "pg_am_oid_index", + "pg_amop_opr_fam_index", + "pg_amop_fam_strat_index", + "pg_amproc_fam_proc_index", + "pg_attribute_relid_attnam_index", + "pg_attribute_relid_attnum_index", + "pg_auth_members_member_role_index", + "pg_auth_members_role_member_index", + "pg_authid_rolname_index", + "pg_authid_oid_index", + "pg_cast_source_target_index", + "pg_opclass_am_name_nsp_index", + "pg_opclass_oid_index", + "pg_collation_name_enc_nsp_index", + "pg_collation_oid_index", + "pg_conversion_default_index", + "pg_conversion_name_nsp_index", + "pg_constraint_oid_index", + "pg_conversion_oid_index", + "pg_database_oid_index", + "pg_default_acl_role_nsp_obj_index", + "pg_enum_oid_index", + "pg_enum_typid_label_index", + "pg_event_trigger_evtname_index", + "pg_event_trigger_oid_index", + "pg_foreign_data_wrapper_name_index", + "pg_foreign_data_wrapper_oid_index", + "pg_foreign_server_name_index", + "pg_foreign_server_oid_index", + "pg_foreign_table_relid_index", + "pg_index_indexrelid_index", + "pg_language_name_index", + "pg_language_oid_index", + "pg_namespace_nspname_index", + "pg_namespace_oid_index", + "pg_operator_oprname_l_r_n_index", + "pg_operator_oid_index", + "pg_opfamily_am_name_nsp_index", + "pg_opfamily_oid_index", + "pg_partitioned_table_partrelid_index", + "pg_proc_proname_args_nsp_index", + "pg_proc_oid_index", + "pg_publication_pubname_index", + "pg_publication_oid_index", + "pg_publication_rel_oid_index", + "pg_publication_rel_prrelid_prpubid_index", + "pg_range_rngtypid_index", + "pg_class_relname_nsp_index", + "pg_class_oid_index", + "pg_replication_origin_roiident_index", + "pg_replication_origin_roname_index", + "pg_rewrite_rel_rulename_index", + "pg_sequence_seqrelid_index", + "pg_statistic_ext_name_index", + "pg_statistic_ext_oid_index", + "pg_statistic_relid_att_inh_index", + "pg_subscription_subname_index", + "pg_subscription_oid_index", + "pg_subscription_rel_srrelid_srsubid_index", + "pg_tablespace_oid_index", + "pg_transform_oid_index", + "pg_transform_type_lang_index", + "pg_ts_config_map_index", + "pg_ts_config_cfgname_index", + "pg_ts_config_oid_index", + "pg_ts_dict_dictname_index", + "pg_ts_dict_oid_index", + "pg_ts_parser_prsname_index", + "pg_ts_parser_oid_index", + "pg_ts_template_tmplname_index", + "pg_ts_template_oid_index", + "pg_type_typname_nsp_index", + "pg_type_oid_index", + "pg_user_mapping_oid_index", + "pg_user_mapping_user_server_index", + "pg_yb_tablegroup_oid_index", + "pg_constraint_conrelid_contypid_conname_index", +}; +static_assert(SysCacheSize == sizeof(yb_cache_index_name_table) / + sizeof(const char *), "Wrong catalog cache number"); + typedef struct YbPinnedObjectKey { Oid classid; @@ -1903,3 +1989,44 @@ oid_compare(const void *a, const void *b) return 0; return (oa > ob) ? 1 : -1; } + +/* + * Verify the table yb_cache_index_name_table is consistent with cacheinfo. + * Should only be invoked when pg_class is fully loaded for SearchSysCache1 + * to find these indexes. + */ +#ifndef NDEBUG +bool +YbCheckCatalogCacheIndexNameTable() +{ + /* + * We can only do this verification during initdb because otherwise + * during YSQL upgrade we can see assertion failure. + */ + if (!YBCIsInitDbModeEnvVarSet()) + return true; + int cache_id; + for (cache_id = 0; cache_id < SysCacheSize; cache_id++) + { + const char* index_name = yb_cache_index_name_table[cache_id]; + Oid indoid = cacheinfo[cache_id].indoid; + HeapTuple tuple = SearchSysCache1(RELOID, indoid); + Assert(HeapTupleIsValid(tuple)); + Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); + if (strcmp(NameStr(classForm->relname), index_name)) + { + ReleaseSysCache(tuple); + YBC_LOG_WARNING("Cache id %u has name mismatch: %s vs %s", cache_id, + NameStr(classForm->relname), index_name); + return false; + } + ReleaseSysCache(tuple); + } + return true; +} +#endif + +const char* YbGetCatalogCacheIndexName(int cache_id) +{ + return yb_cache_index_name_table[cache_id]; +} diff --git a/src/postgres/src/include/utils/catcache.h b/src/postgres/src/include/utils/catcache.h index b01440de607f..8ad036977156 100644 --- a/src/postgres/src/include/utils/catcache.h +++ b/src/postgres/src/include/utils/catcache.h @@ -241,7 +241,8 @@ extern bool RelationHasCachedLists(Relation relation); extern void PrintCatCacheLeakWarning(HeapTuple tuple); extern void PrintCatCacheListLeakWarning(CatCList *list); -extern long GetCatCacheMisses(); +extern long YbGetCatCacheMisses(); +extern long* YbGetCatCacheIdMisses(); extern YbCatCListIterator YbCatCListIteratorBegin(CatCList *list); extern HeapTuple YbCatCListIteratorGetNext(YbCatCListIterator *iterator); diff --git a/src/postgres/src/include/utils/syscache.h b/src/postgres/src/include/utils/syscache.h index b70c66580eef..db93dfcf7e4a 100644 --- a/src/postgres/src/include/utils/syscache.h +++ b/src/postgres/src/include/utils/syscache.h @@ -123,6 +123,10 @@ extern void YbInitPinnedCacheIfNeeded(bool shared_only); extern void YbResetPinnedCache(); extern bool YbIsObjectPinned(Oid classId, Oid objectId, bool shared_dependency); extern void YbPinObjectIfNeeded(Oid classId, Oid objectId, bool shared_dependency); +#ifndef NDEBUG +extern bool YbCheckCatalogCacheIndexNameTable(); +#endif +extern const char* YbGetCatalogCacheIndexName(int cache_id); extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); diff --git a/src/postgres/yb-extensions/yb_pg_metrics/yb_pg_metrics.c b/src/postgres/yb-extensions/yb_pg_metrics/yb_pg_metrics.c index a39fc449dd89..bc9ebb5be2a0 100644 --- a/src/postgres/yb-extensions/yb_pg_metrics/yb_pg_metrics.c +++ b/src/postgres/yb-extensions/yb_pg_metrics/yb_pg_metrics.c @@ -12,6 +12,7 @@ #include "postgres.h" +#include #include #include #include @@ -61,6 +62,87 @@ typedef enum statementType Transaction, AggregatePushdown, CatCacheMisses, + CatCacheMisses_Start, + CatCacheMisses_0 = CatCacheMisses_Start, + CatCacheMisses_1, + CatCacheMisses_2, + CatCacheMisses_3, + CatCacheMisses_4, + CatCacheMisses_5, + CatCacheMisses_6, + CatCacheMisses_7, + CatCacheMisses_8, + CatCacheMisses_9, + CatCacheMisses_10, + CatCacheMisses_11, + CatCacheMisses_12, + CatCacheMisses_13, + CatCacheMisses_14, + CatCacheMisses_15, + CatCacheMisses_16, + CatCacheMisses_17, + CatCacheMisses_18, + CatCacheMisses_19, + CatCacheMisses_20, + CatCacheMisses_21, + CatCacheMisses_22, + CatCacheMisses_23, + CatCacheMisses_24, + CatCacheMisses_25, + CatCacheMisses_26, + CatCacheMisses_27, + CatCacheMisses_28, + CatCacheMisses_29, + CatCacheMisses_30, + CatCacheMisses_31, + CatCacheMisses_32, + CatCacheMisses_33, + CatCacheMisses_34, + CatCacheMisses_35, + CatCacheMisses_36, + CatCacheMisses_37, + CatCacheMisses_38, + CatCacheMisses_39, + CatCacheMisses_40, + CatCacheMisses_41, + CatCacheMisses_42, + CatCacheMisses_43, + CatCacheMisses_44, + CatCacheMisses_45, + CatCacheMisses_46, + CatCacheMisses_47, + CatCacheMisses_48, + CatCacheMisses_49, + CatCacheMisses_50, + CatCacheMisses_51, + CatCacheMisses_52, + CatCacheMisses_53, + CatCacheMisses_54, + CatCacheMisses_55, + CatCacheMisses_56, + CatCacheMisses_57, + CatCacheMisses_58, + CatCacheMisses_59, + CatCacheMisses_60, + CatCacheMisses_61, + CatCacheMisses_62, + CatCacheMisses_63, + CatCacheMisses_64, + CatCacheMisses_65, + CatCacheMisses_66, + CatCacheMisses_67, + CatCacheMisses_68, + CatCacheMisses_69, + CatCacheMisses_70, + CatCacheMisses_71, + CatCacheMisses_72, + CatCacheMisses_73, + CatCacheMisses_74, + CatCacheMisses_75, + CatCacheMisses_76, + CatCacheMisses_77, + CatCacheMisses_78, + CatCacheMisses_End, kMaxStatementType } statementType; int num_entries = kMaxStatementType; @@ -103,6 +185,7 @@ PgBackendStatus *backendStatusArray = NULL; extern int MaxConnections; static long last_cache_misses_val = 0; +static long last_cache_id_misses_val[SysCacheSize] = {0}; static volatile sig_atomic_t got_SIGHUP = false; static volatile sig_atomic_t got_SIGTERM = false; @@ -195,6 +278,17 @@ set_metric_names(void) strcpy(ybpgm_table[Transaction].name, YSQL_METRIC_PREFIX "Transactions"); strcpy(ybpgm_table[AggregatePushdown].name, YSQL_METRIC_PREFIX "AggregatePushdowns"); strcpy(ybpgm_table[CatCacheMisses].name, YSQL_METRIC_PREFIX "CatalogCacheMisses"); + for (int i = CatCacheMisses_Start; i < CatCacheMisses_End; ++i) + { + int cache_id = i - CatCacheMisses_Start; + char index_name[NAMEDATALEN + 16]; + strcpy(ybpgm_table[i].name, YSQL_METRIC_PREFIX "CatalogCacheMisses"); + sprintf(index_name, "_%d_%s", + cache_id, YbGetCatalogCacheIndexName(cache_id)); + Assert(strlen(ybpgm_table[i].name) + strlen(index_name) < + sizeof(ybpgm_table[i].name)); + strcat(ybpgm_table[i].name, index_name); + } } /* @@ -530,6 +624,8 @@ _PG_init(void) prev_ProcessUtility = ProcessUtility_hook; ProcessUtility_hook = ybpgm_ProcessUtility; + static_assert(SysCacheSize == CatCacheMisses_End - CatCacheMisses_Start, + "Wrong catalog cache number"); } /* @@ -663,27 +759,37 @@ ybpgm_ExecutorEnd(QueryDesc *queryDesc) ybpgm_Store(type, time, rows_count); - if (queryDesc->estate->yb_es_is_single_row_modify_txn) - { - ybpgm_Store(Single_Shard_Transaction, time, rows_count); - ybpgm_Store(SingleShardTransaction, time, rows_count); - } - - if (!is_inside_transaction_block) - ybpgm_Store(Transaction, time, rows_count); - - if (IsA(queryDesc->planstate, AggState) && - castNode(AggState, queryDesc->planstate)->yb_pushdown_supported) - ybpgm_Store(AggregatePushdown, time, rows_count); - - long current_cache_misses = GetCatCacheMisses(); + if (queryDesc->estate->yb_es_is_single_row_modify_txn) + { + ybpgm_Store(Single_Shard_Transaction, time, rows_count); + ybpgm_Store(SingleShardTransaction, time, rows_count); + } - /* Currently we set the time parameter to 0 as we don't have metrics - * for that available - * TODO: Get timing metrics for catalog cache misses - */ - ybpgm_StoreCount(CatCacheMisses, 0, current_cache_misses - last_cache_misses_val); - last_cache_misses_val = current_cache_misses; + if (!is_inside_transaction_block) + ybpgm_Store(Transaction, time, rows_count); + + if (IsA(queryDesc->planstate, AggState) && + castNode(AggState, queryDesc->planstate)->yb_pushdown_supported) + ybpgm_Store(AggregatePushdown, time, rows_count); + + long current_cache_misses = YbGetCatCacheMisses(); + long* current_cache_id_misses = YbGetCatCacheIdMisses(); + + long total_delta = current_cache_misses - last_cache_misses_val; + last_cache_misses_val = current_cache_misses; + + /* Currently we set the time parameter to 0 as we don't have metrics + * for that available + * TODO: Get timing metrics for catalog cache misses + */ + ybpgm_StoreCount(CatCacheMisses, 0, total_delta); + if (total_delta > 0) + for (int i = CatCacheMisses_Start; i < CatCacheMisses_End; ++i) + { + int j = i - CatCacheMisses_Start; + ybpgm_StoreCount(i, 0, current_cache_id_misses[j] - last_cache_id_misses_val[j]); + last_cache_id_misses_val[j] = current_cache_id_misses[j]; + } } IncStatementNestingLevel(); diff --git a/src/yb/yql/pggate/webserver/pgsql_webserver_wrapper.h b/src/yb/yql/pggate/webserver/pgsql_webserver_wrapper.h index 8a96c929b839..65aa381aa558 100644 --- a/src/yb/yql/pggate/webserver/pgsql_webserver_wrapper.h +++ b/src/yb/yql/pggate/webserver/pgsql_webserver_wrapper.h @@ -34,7 +34,7 @@ extern "C" { struct WebserverWrapper; typedef struct ybpgmEntry { - char name[100]; + char name[120]; YB_ATOMIC_ULLONG calls; YB_ATOMIC_ULLONG total_time; YB_ATOMIC_ULLONG rows; diff --git a/src/yb/yql/pgwrapper/pg_libpq-test.cc b/src/yb/yql/pgwrapper/pg_libpq-test.cc index a960572132b9..12cc6b8747ca 100644 --- a/src/yb/yql/pgwrapper/pg_libpq-test.cc +++ b/src/yb/yql/pgwrapper/pg_libpq-test.cc @@ -3948,6 +3948,53 @@ TEST_F(PgLibPqTest, CatalogCacheMemoryLeak) { } } +static int GetCacheMissCount(const string& metric_instance) { + auto begin = metric_instance.find("} "); + int count; + std::istringstream(metric_instance.substr(begin + 2)) >> count; + return count; +} + +TEST_F(PgLibPqTest, CatalogCacheIdMissMetricsTest) { + auto conn = ASSERT_RESULT(Connect()); + // Make a new connection to see more cache misses (by default we will only + // preload the catalog caches for the first connection). + conn = ASSERT_RESULT(Connect()); + ASSERT_OK(conn.Execute("CREATE TABLE t (key INT, value TEXT)")); + ASSERT_OK(conn.Execute("INSERT INTO t (key, value) VALUES (1, 'hello')")); + auto result = ASSERT_RESULT(conn.Fetch("SELECT * FROM t")); + ExternalTabletServer* ts = cluster_->tablet_server(0); + auto hostport = Format("$0:$1", ts->bind_host(), ts->pgsql_http_port()); + auto pg_metrics_url = Substitute( + "http://$0/prometheus-metrics?reset_histograms=false&show_help=false", + hostport); + EasyCurl c; + faststring buf; + ASSERT_OK(c.FetchURL(pg_metrics_url, &buf)); + string page_content = buf.ToString(); + auto cache_miss_metric = + "handler_latency_yb_ysqlserver_SQLProcessor_CatalogCacheMisses_count"; + auto begin = page_content.find(cache_miss_metric); + auto end = page_content.find("\n", begin); + auto expected = GetCacheMissCount(page_content.substr(begin, end - begin)); + ASSERT_GT(expected, 0); + LOG(INFO) << "Expected total cache misses: " << expected; + int total_cache_misses = 0; + for (int i = 0; ; ++i) { + auto cache_miss_metric_id = + Format("handler_latency_yb_ysqlserver_SQLProcessor_CatalogCacheMisses_$0_", i); + begin = page_content.find(cache_miss_metric_id); + if (begin == std::string::npos) { + break; + } + end = page_content.find("\n", begin); + auto cache_misses = GetCacheMissCount(page_content.substr(begin, end - begin)); + ASSERT_GE(cache_misses, 0); + total_cache_misses += cache_misses; + } + ASSERT_EQ(total_cache_misses, expected); +} + class PgLibPqCreateSequenceNamespaceRaceTest : public PgLibPqTest { void UpdateMiniClusterOptions(ExternalMiniClusterOptions* options) override { options->extra_tserver_flags.push_back(