postgresai 0.14.0-dev.45 → 0.14.0-dev.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/checkup.ts CHANGED
@@ -780,6 +780,276 @@ export async function generateH004(client: Client, nodeName: string = "node-01")
780
780
  return report;
781
781
  }
782
782
 
783
+ /**
784
+ * Generate D004 report - pg_stat_statements and pg_stat_kcache settings
785
+ */
786
+ async function generateD004(client: Client, nodeName: string): Promise<Report> {
787
+ const report = createBaseReport("D004", "pg_stat_statements and pg_stat_kcache settings", nodeName);
788
+ const postgresVersion = await getPostgresVersion(client);
789
+ const allSettings = await getSettings(client);
790
+
791
+ // Filter settings related to pg_stat_statements and pg_stat_kcache
792
+ const pgssSettings: Record<string, SettingInfo> = {};
793
+ for (const [name, setting] of Object.entries(allSettings)) {
794
+ if (name.startsWith("pg_stat_statements") || name.startsWith("pg_stat_kcache")) {
795
+ pgssSettings[name] = setting;
796
+ }
797
+ }
798
+
799
+ // Check pg_stat_statements extension
800
+ let pgssAvailable = false;
801
+ let pgssMetricsCount = 0;
802
+ let pgssTotalCalls = 0;
803
+ const pgssSampleQueries: Array<{ queryid: string; user: string; database: string; calls: number }> = [];
804
+
805
+ try {
806
+ const extCheck = await client.query(
807
+ "select 1 from pg_extension where extname = 'pg_stat_statements'"
808
+ );
809
+ if (extCheck.rows.length > 0) {
810
+ pgssAvailable = true;
811
+ const statsResult = await client.query(`
812
+ select count(*) as cnt, coalesce(sum(calls), 0) as total_calls
813
+ from pg_stat_statements
814
+ `);
815
+ pgssMetricsCount = parseInt(statsResult.rows[0]?.cnt || "0", 10);
816
+ pgssTotalCalls = parseInt(statsResult.rows[0]?.total_calls || "0", 10);
817
+
818
+ // Get sample queries (top 5 by calls)
819
+ const sampleResult = await client.query(`
820
+ select
821
+ queryid::text as queryid,
822
+ coalesce(usename, 'unknown') as "user",
823
+ coalesce(datname, 'unknown') as database,
824
+ calls
825
+ from pg_stat_statements s
826
+ left join pg_database d on s.dbid = d.oid
827
+ left join pg_user u on s.userid = u.usesysid
828
+ order by calls desc
829
+ limit 5
830
+ `);
831
+ for (const row of sampleResult.rows) {
832
+ pgssSampleQueries.push({
833
+ queryid: row.queryid,
834
+ user: row.user,
835
+ database: row.database,
836
+ calls: parseInt(row.calls, 10),
837
+ });
838
+ }
839
+ }
840
+ } catch {
841
+ // Extension not available or accessible
842
+ }
843
+
844
+ // Check pg_stat_kcache extension
845
+ let kcacheAvailable = false;
846
+ let kcacheMetricsCount = 0;
847
+ let kcacheTotalExecTime = 0;
848
+ let kcacheTotalUserTime = 0;
849
+ let kcacheTotalSystemTime = 0;
850
+ const kcacheSampleQueries: Array<{ queryid: string; user: string; exec_total_time: number }> = [];
851
+
852
+ try {
853
+ const extCheck = await client.query(
854
+ "select 1 from pg_extension where extname = 'pg_stat_kcache'"
855
+ );
856
+ if (extCheck.rows.length > 0) {
857
+ kcacheAvailable = true;
858
+ const statsResult = await client.query(`
859
+ select
860
+ count(*) as cnt,
861
+ coalesce(sum(exec_user_time + exec_system_time), 0) as total_exec_time,
862
+ coalesce(sum(exec_user_time), 0) as total_user_time,
863
+ coalesce(sum(exec_system_time), 0) as total_system_time
864
+ from pg_stat_kcache
865
+ `);
866
+ kcacheMetricsCount = parseInt(statsResult.rows[0]?.cnt || "0", 10);
867
+ kcacheTotalExecTime = parseFloat(statsResult.rows[0]?.total_exec_time || "0");
868
+ kcacheTotalUserTime = parseFloat(statsResult.rows[0]?.total_user_time || "0");
869
+ kcacheTotalSystemTime = parseFloat(statsResult.rows[0]?.total_system_time || "0");
870
+
871
+ // Get sample queries (top 5 by exec time)
872
+ const sampleResult = await client.query(`
873
+ select
874
+ queryid::text as queryid,
875
+ coalesce(usename, 'unknown') as "user",
876
+ (exec_user_time + exec_system_time) as exec_total_time
877
+ from pg_stat_kcache k
878
+ left join pg_user u on k.userid = u.usesysid
879
+ order by (exec_user_time + exec_system_time) desc
880
+ limit 5
881
+ `);
882
+ for (const row of sampleResult.rows) {
883
+ kcacheSampleQueries.push({
884
+ queryid: row.queryid,
885
+ user: row.user,
886
+ exec_total_time: parseFloat(row.exec_total_time),
887
+ });
888
+ }
889
+ }
890
+ } catch {
891
+ // Extension not available or accessible
892
+ }
893
+
894
+ report.results[nodeName] = {
895
+ data: {
896
+ settings: pgssSettings,
897
+ pg_stat_statements_status: {
898
+ extension_available: pgssAvailable,
899
+ metrics_count: pgssMetricsCount,
900
+ total_calls: pgssTotalCalls,
901
+ sample_queries: pgssSampleQueries,
902
+ },
903
+ pg_stat_kcache_status: {
904
+ extension_available: kcacheAvailable,
905
+ metrics_count: kcacheMetricsCount,
906
+ total_exec_time: kcacheTotalExecTime,
907
+ total_user_time: kcacheTotalUserTime,
908
+ total_system_time: kcacheTotalSystemTime,
909
+ sample_queries: kcacheSampleQueries,
910
+ },
911
+ },
912
+ postgres_version: postgresVersion,
913
+ };
914
+
915
+ return report;
916
+ }
917
+
918
+ /**
919
+ * Generate F001 report - Autovacuum: current settings
920
+ */
921
+ async function generateF001(client: Client, nodeName: string): Promise<Report> {
922
+ const report = createBaseReport("F001", "Autovacuum: current settings", nodeName);
923
+ const postgresVersion = await getPostgresVersion(client);
924
+ const allSettings = await getSettings(client);
925
+
926
+ // Filter autovacuum-related settings
927
+ const autovacuumSettings: Record<string, SettingInfo> = {};
928
+ for (const [name, setting] of Object.entries(allSettings)) {
929
+ if (name.includes("autovacuum") || name.includes("vacuum")) {
930
+ autovacuumSettings[name] = setting;
931
+ }
932
+ }
933
+
934
+ report.results[nodeName] = {
935
+ data: autovacuumSettings,
936
+ postgres_version: postgresVersion,
937
+ };
938
+
939
+ return report;
940
+ }
941
+
942
+ /**
943
+ * Generate G001 report - Memory-related settings
944
+ */
945
+ async function generateG001(client: Client, nodeName: string): Promise<Report> {
946
+ const report = createBaseReport("G001", "Memory-related settings", nodeName);
947
+ const postgresVersion = await getPostgresVersion(client);
948
+ const allSettings = await getSettings(client);
949
+
950
+ // Memory-related setting names
951
+ const memorySettingNames = [
952
+ "shared_buffers",
953
+ "work_mem",
954
+ "maintenance_work_mem",
955
+ "effective_cache_size",
956
+ "wal_buffers",
957
+ "temp_buffers",
958
+ "max_connections",
959
+ "autovacuum_work_mem",
960
+ "hash_mem_multiplier",
961
+ "logical_decoding_work_mem",
962
+ "max_stack_depth",
963
+ "max_prepared_transactions",
964
+ "max_locks_per_transaction",
965
+ "max_pred_locks_per_transaction",
966
+ ];
967
+
968
+ const memorySettings: Record<string, SettingInfo> = {};
969
+ for (const name of memorySettingNames) {
970
+ if (allSettings[name]) {
971
+ memorySettings[name] = allSettings[name];
972
+ }
973
+ }
974
+
975
+ // Calculate memory usage estimates
976
+ interface MemoryUsage {
977
+ shared_buffers_bytes: number;
978
+ shared_buffers_pretty: string;
979
+ wal_buffers_bytes: number;
980
+ wal_buffers_pretty: string;
981
+ shared_memory_total_bytes: number;
982
+ shared_memory_total_pretty: string;
983
+ work_mem_per_connection_bytes: number;
984
+ work_mem_per_connection_pretty: string;
985
+ max_work_mem_usage_bytes: number;
986
+ max_work_mem_usage_pretty: string;
987
+ maintenance_work_mem_bytes: number;
988
+ maintenance_work_mem_pretty: string;
989
+ effective_cache_size_bytes: number;
990
+ effective_cache_size_pretty: string;
991
+ }
992
+
993
+ let memoryUsage: MemoryUsage | Record<string, never> = {};
994
+
995
+ try {
996
+ // Get actual byte values from PostgreSQL
997
+ const memQuery = await client.query(`
998
+ select
999
+ pg_size_bytes(current_setting('shared_buffers')) as shared_buffers_bytes,
1000
+ pg_size_bytes(current_setting('wal_buffers')) as wal_buffers_bytes,
1001
+ pg_size_bytes(current_setting('work_mem')) as work_mem_bytes,
1002
+ pg_size_bytes(current_setting('maintenance_work_mem')) as maintenance_work_mem_bytes,
1003
+ pg_size_bytes(current_setting('effective_cache_size')) as effective_cache_size_bytes,
1004
+ current_setting('max_connections')::int as max_connections
1005
+ `);
1006
+
1007
+ if (memQuery.rows.length > 0) {
1008
+ const row = memQuery.rows[0];
1009
+ const sharedBuffersBytes = parseInt(row.shared_buffers_bytes, 10);
1010
+ const walBuffersBytes = parseInt(row.wal_buffers_bytes, 10);
1011
+ const workMemBytes = parseInt(row.work_mem_bytes, 10);
1012
+ const maintenanceWorkMemBytes = parseInt(row.maintenance_work_mem_bytes, 10);
1013
+ const effectiveCacheSizeBytes = parseInt(row.effective_cache_size_bytes, 10);
1014
+ const maxConnections = row.max_connections;
1015
+
1016
+ const sharedMemoryTotal = sharedBuffersBytes + walBuffersBytes;
1017
+ const maxWorkMemUsage = workMemBytes * maxConnections;
1018
+
1019
+ memoryUsage = {
1020
+ shared_buffers_bytes: sharedBuffersBytes,
1021
+ shared_buffers_pretty: formatBytes(sharedBuffersBytes),
1022
+ wal_buffers_bytes: walBuffersBytes,
1023
+ wal_buffers_pretty: formatBytes(walBuffersBytes),
1024
+ shared_memory_total_bytes: sharedMemoryTotal,
1025
+ shared_memory_total_pretty: formatBytes(sharedMemoryTotal),
1026
+ work_mem_per_connection_bytes: workMemBytes,
1027
+ work_mem_per_connection_pretty: formatBytes(workMemBytes),
1028
+ max_work_mem_usage_bytes: maxWorkMemUsage,
1029
+ max_work_mem_usage_pretty: formatBytes(maxWorkMemUsage),
1030
+ maintenance_work_mem_bytes: maintenanceWorkMemBytes,
1031
+ maintenance_work_mem_pretty: formatBytes(maintenanceWorkMemBytes),
1032
+ effective_cache_size_bytes: effectiveCacheSizeBytes,
1033
+ effective_cache_size_pretty: formatBytes(effectiveCacheSizeBytes),
1034
+ };
1035
+ }
1036
+ } catch {
1037
+ // If we can't calculate, leave empty object (schema allows this)
1038
+ }
1039
+
1040
+ report.results[nodeName] = {
1041
+ data: {
1042
+ settings: memorySettings,
1043
+ analysis: {
1044
+ estimated_total_memory_usage: memoryUsage,
1045
+ },
1046
+ },
1047
+ postgres_version: postgresVersion,
1048
+ };
1049
+
1050
+ return report;
1051
+ }
1052
+
783
1053
  /**
784
1054
  * Available report generators
785
1055
  */
@@ -789,6 +1059,9 @@ export const REPORT_GENERATORS: Record<string, (client: Client, nodeName: string
789
1059
  A004: generateA004,
790
1060
  A007: generateA007,
791
1061
  A013: generateA013,
1062
+ D004: generateD004,
1063
+ F001: generateF001,
1064
+ G001: generateG001,
792
1065
  H001: generateH001,
793
1066
  H002: generateH002,
794
1067
  H004: generateH004,
@@ -803,6 +1076,9 @@ export const CHECK_INFO: Record<string, string> = {
803
1076
  A004: "Cluster information",
804
1077
  A007: "Altered settings",
805
1078
  A013: "Postgres minor version",
1079
+ D004: "pg_stat_statements and pg_stat_kcache settings",
1080
+ F001: "Autovacuum: current settings",
1081
+ G001: "Memory-related settings",
806
1082
  H001: "Invalid indexes",
807
1083
  H002: "Unused indexes",
808
1084
  H004: "Redundant indexes",