11import { LogId } from "../logger.js" ;
22import type { ApiClient } from "./apiClient.js" ;
33import { getProcessIdFromCluster } from "./cluster.js" ;
4+ import type { components } from "./openapi.js" ;
45
5- export enum PerformanceAdvisorOperation {
6- SUGGESTED_INDEXES = "suggestedIndexes" ,
7- DROP_INDEX_SUGGESTIONS = "dropIndexSuggestions" ,
8- SLOW_QUERY_LOGS = "slowQueryLogs" ,
9- SCHEMA_SUGGESTIONS = "schemaSuggestions" ,
10- }
11-
12- export interface SuggestedIndex {
13- avgObjSize ?: number ;
14- id ?: string ;
15- impact ?: Array < string > ;
16- index ?: Array < { [ key : string ] : 1 | - 1 } > ;
17- namespace ?: string ;
18- weight ?: number ;
19- }
6+ export type SuggestedIndex = components [ "schemas" ] [ "PerformanceAdvisorIndex" ] ;
7+ export type DropIndexSuggestion = components [ "schemas" ] [ "DropIndexSuggestionsIndex" ] ;
8+ export type SlowQueryLogMetrics = components [ "schemas" ] [ "PerformanceAdvisorSlowQueryMetrics" ] ;
9+ export type SlowQueryLog = components [ "schemas" ] [ "PerformanceAdvisorSlowQuery" ] ;
2010
2111interface SuggestedIndexesResponse {
22- content : {
23- suggestedIndexes ?: Array < SuggestedIndex > ;
24- } ;
12+ content : components [ "schemas" ] [ "PerformanceAdvisorResponse" ] ;
2513}
26-
2714interface DropIndexesResponse {
28- content : {
29- hiddenIndexes ?: Array < DropIndexSuggestion > ;
30- redundantIndexes ?: Array < DropIndexSuggestion > ;
31- unusedIndexes ?: Array < DropIndexSuggestion > ;
32- } ;
15+ content : components [ "schemas" ] [ "DropIndexSuggestionsResponse" ] ;
3316}
34-
3517interface SchemaAdviceResponse {
36- content : {
37- recommendations ?: Array < SchemaRecommendation > ;
38- } ;
39- }
40-
41- interface SlowQueriesResponse {
42- slowQueries ?: Array < SlowQueryLog > ;
43- }
44-
45- export interface DropIndexSuggestion {
46- accessCount ?: number ;
47- index ?: Array < { [ key : string ] : 1 | - 1 } > ;
48- name ?: string ;
49- namespace ?: string ;
50- shards ?: Array < string > ;
51- since ?: string ;
52- sizeBytes ?: number ;
53- }
54-
55- export type SchemaTriggerType =
56- | "PERCENT_QUERIES_USE_LOOKUP"
57- | "NUMBER_OF_QUERIES_USE_LOOKUP"
58- | "DOCS_CONTAIN_UNBOUNDED_ARRAY"
59- | "NUMBER_OF_NAMESPACES"
60- | "DOC_SIZE_TOO_LARGE"
61- | "NUM_INDEXES"
62- | "QUERIES_CONTAIN_CASE_INSENSITIVE_REGEX" ;
63-
64- export const SCHEMA_TRIGGER_DESCRIPTIONS : Record < SchemaTriggerType , string > = {
65- PERCENT_QUERIES_USE_LOOKUP : "High percentage of queries (>50%) use $lookup operations" ,
66- NUMBER_OF_QUERIES_USE_LOOKUP : "High number of queries (>100) use $lookup operations" ,
67- DOCS_CONTAIN_UNBOUNDED_ARRAY : "Arrays with over 10000 entries detected in the collection(s)" ,
68- NUMBER_OF_NAMESPACES : "Too many namespaces (collections) in the database (>100)" ,
69- DOC_SIZE_TOO_LARGE : "Documents larger than 2 MB found in the collection(s)" ,
70- NUM_INDEXES : "More than 30 indexes detected in the collection(s) scanned" ,
71- QUERIES_CONTAIN_CASE_INSENSITIVE_REGEX : "Queries use case-insensitive regular expressions" ,
72- } ;
73-
74- type SchemaRecommedationType =
75- | "REDUCE_LOOKUP_OPS"
76- | "AVOID_UNBOUNDED_ARRAY"
77- | "REDUCE_DOCUMENT_SIZE"
78- | "REMOVE_UNNECESSARY_INDEXES"
79- | "REDUCE_NUMBER_OF_NAMESPACES"
80- | "OPTIMIZE_CASE_INSENSITIVE_REGEX_QUERIES"
81- | "OPTIMIZE_TEXT_QUERIES" ;
82-
83- export const SCHEMA_RECOMMENDATION_DESCRIPTIONS : Record < SchemaRecommedationType , string > = {
84- REDUCE_LOOKUP_OPS : "Reduce the use of $lookup operations" ,
85- AVOID_UNBOUNDED_ARRAY : "Avoid using unbounded arrays in documents" ,
86- REDUCE_DOCUMENT_SIZE : "Reduce the size of documents" ,
87- REMOVE_UNNECESSARY_INDEXES : "Remove unnecessary indexes" ,
88- REDUCE_NUMBER_OF_NAMESPACES : "Reduce the number of collections in the database" ,
89- OPTIMIZE_CASE_INSENSITIVE_REGEX_QUERIES : "Optimize case-insensitive regex queries" ,
90- OPTIMIZE_TEXT_QUERIES : "Optimize text search queries" ,
91- } ;
92-
93- export interface SchemaRecommendation {
94- affectedNamespaces ?: Array < {
95- namespace ?: string | null ;
96- triggers ?: Array < {
97- description ?: string ;
98- triggerType ?: SchemaTriggerType ;
99- } > ;
100- } > ;
101- description ?: string ;
102- recommendation ?: SchemaRecommedationType ;
103- }
104-
105- interface SlowQueryLogMetrics {
106- docsExamined ?: number ;
107- docsExaminedReturnedRatio ?: number ;
108- docsReturned ?: number ;
109- fromUserConnection ?: boolean ;
110- hasIndexCoverage ?: boolean ;
111- hasSort ?: boolean ;
112- keysExamined ?: number ;
113- keysExaminedReturnedRatio ?: number ;
114- numYields ?: number ;
115- operationExecutionTime ?: number ;
116- responseLength ?: number ;
117- }
118-
119- export interface SlowQueryLog {
120- line ?: string ;
121- metrics ?: SlowQueryLogMetrics ;
122- namespace ?: string ;
123- opType ?: string ;
124- replicaState ?: string ;
125- }
126-
127- export interface PerformanceAdvisorData {
128- suggestedIndexes : Array < SuggestedIndex > ;
129- dropIndexSuggestions : {
130- hiddenIndexes : Array < DropIndexSuggestion > ;
131- redundantIndexes : Array < DropIndexSuggestion > ;
132- unusedIndexes : Array < DropIndexSuggestion > ;
133- } ;
134- slowQueryLogs : Array < SlowQueryLog > ;
135- schemaSuggestions : Array < SchemaRecommendation > ;
18+ content : components [ "schemas" ] [ "SchemaAdvisorResponse" ] ;
13619}
20+ export type SchemaRecommendation = components [ "schemas" ] [ "SchemaAdvisorItemRecommendation" ] ;
13721
13822export async function getSuggestedIndexes (
13923 apiClient : ApiClient ,
@@ -243,7 +127,7 @@ export async function getSlowQueries(
243127 } ,
244128 } ) ;
245129
246- return { slowQueryLogs : ( response as SlowQueriesResponse ) . slowQueries ?? [ ] } ;
130+ return { slowQueryLogs : response . slowQueries ?? [ ] } ;
247131 } catch ( err ) {
248132 apiClient . logger . debug ( {
249133 id : LogId . atlasPaSlowQueryLogsFailure ,
@@ -253,106 +137,3 @@ export async function getSlowQueries(
253137 throw new Error ( `Failed to list slow query logs: ${ err instanceof Error ? err . message : String ( err ) } ` ) ;
254138 }
255139}
256-
257- export function formatSuggestedIndexesTable ( suggestedIndexes : Array < SuggestedIndex > ) : string {
258- if ( suggestedIndexes . length === 0 ) return "No suggested indexes found." ;
259-
260- const rows = suggestedIndexes
261- . map ( ( index , i ) => {
262- const namespace = index . namespace ?? "N/A" ;
263- const weight = index . weight ?? "N/A" ;
264- const avgObjSize = index . avgObjSize ?? "N/A" ;
265- const indexKeys = index . index ? index . index . map ( ( key ) => Object . keys ( key ) [ 0 ] ) . join ( ", " ) : "N/A" ;
266- return `${ i + 1 } | ${ namespace } | ${ weight } | ${ avgObjSize } | ${ indexKeys } ` ;
267- } )
268- . join ( "\n" ) ;
269-
270- return `# | Namespace | Weight | Avg Obj Size | Index Keys
271- ---|-----------|--------|--------------|------------
272- ${ rows } `;
273- }
274-
275- export function formatDropIndexesTable ( dropIndexSuggestions : {
276- hiddenIndexes : Array < DropIndexSuggestion > ;
277- redundantIndexes : Array < DropIndexSuggestion > ;
278- unusedIndexes : Array < DropIndexSuggestion > ;
279- } ) : string {
280- const allIndexes = [
281- ...dropIndexSuggestions . hiddenIndexes . map ( ( index ) => ( { ...index , type : "Hidden" } ) ) ,
282- ...dropIndexSuggestions . redundantIndexes . map ( ( index ) => ( { ...index , type : "Redundant" } ) ) ,
283- ...dropIndexSuggestions . unusedIndexes . map ( ( index ) => ( { ...index , type : "Unused" } ) ) ,
284- ] ;
285-
286- if ( allIndexes . length === 0 ) return "No drop index suggestions found." ;
287-
288- const rows = allIndexes
289- . map ( ( index , i ) => {
290- const name = index . name ?? "N/A" ;
291- const namespace = index . namespace ?? "N/A" ;
292- const type = index . type ?? "N/A" ;
293- const sizeBytes = index . sizeBytes ?? "N/A" ;
294- const accessCount = index . accessCount ?? "N/A" ;
295- return `${ i + 1 } | ${ name } | ${ namespace } | ${ type } | ${ sizeBytes } | ${ accessCount } ` ;
296- } )
297- . join ( "\n" ) ;
298-
299- return `# | Index Name | Namespace | Type | Size (bytes) | Access Count
300- ---|------------|-----------|------|--------------|-------------
301- ${ rows } `;
302- }
303-
304- export function formatSlowQueriesTable ( slowQueryLogs : Array < SlowQueryLog > ) : string {
305- if ( slowQueryLogs . length === 0 ) return "No slow query logs found." ;
306-
307- const rows = slowQueryLogs
308- . map ( ( log , i ) => {
309- const namespace = log . namespace ?? "N/A" ;
310- const opType = log . opType ?? "N/A" ;
311- const executionTime = log . metrics ?. operationExecutionTime ?? "N/A" ;
312- const docsExamined = log . metrics ?. docsExamined ?? "N/A" ;
313- const docsReturned = log . metrics ?. docsReturned ?? "N/A" ;
314- return `${ i + 1 } | ${ namespace } | ${ opType } | ${ executionTime } ms | ${ docsExamined } | ${ docsReturned } ` ;
315- } )
316- . join ( "\n" ) ;
317-
318- return `# | Namespace | Operation | Execution Time | Docs Examined | Docs Returned
319- ---|-----------|-----------|---------------|---------------|---------------
320- ${ rows } `;
321- }
322-
323- function getTriggerDescription ( triggerType : SchemaTriggerType | undefined ) : string {
324- if ( ! triggerType ) return "N/A" ;
325- return SCHEMA_TRIGGER_DESCRIPTIONS [ triggerType ] ?? triggerType ;
326- }
327-
328- function getNamespaceTriggerDescriptions ( namespace : { triggers ?: Array < { triggerType ?: SchemaTriggerType } > } ) : string {
329- if ( ! namespace . triggers ) return "N/A" ;
330-
331- return namespace . triggers . map ( ( trigger ) => getTriggerDescription ( trigger . triggerType ) ) . join ( ", " ) ;
332- }
333-
334- function getTriggerDescriptions ( suggestion : SchemaRecommendation ) : string {
335- if ( ! suggestion . affectedNamespaces ) return "N/A" ;
336-
337- return suggestion . affectedNamespaces . map ( ( namespace ) => getNamespaceTriggerDescriptions ( namespace ) ) . join ( ", " ) ;
338- }
339-
340- export function formatSchemaSuggestionsTable ( schemaSuggestions : Array < SchemaRecommendation > ) : string {
341- if ( schemaSuggestions . length === 0 ) return "No schema suggestions found." ;
342-
343- const rows = schemaSuggestions
344- . map ( ( suggestion : SchemaRecommendation , i ) => {
345- const recommendation = suggestion . recommendation
346- ? ( SCHEMA_RECOMMENDATION_DESCRIPTIONS [ suggestion . recommendation ] ?? suggestion . recommendation )
347- : "N/A" ;
348- const description = suggestion . description ?? "N/A" ;
349- const triggeredBy = getTriggerDescriptions ( suggestion ) ;
350- const affectedNamespaces = suggestion . affectedNamespaces ?. length ?? 0 ;
351- return `${ i + 1 } | ${ recommendation } | ${ description } | ${ triggeredBy } | ${ affectedNamespaces } namespaces` ;
352- } )
353- . join ( "\n" ) ;
354-
355- return `# | Recommendation | Description | Triggered By | Affected Namespaces
356- ---|---------------|-------------|----------|-------------------
357- ${ rows } `;
358- }
0 commit comments