-
Notifications
You must be signed in to change notification settings - Fork 715
fix: UI freezes on enabling histogram #4813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughThe pull request implements substantial modifications to the Changes
Possibly related PRs
Suggested reviewers
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Outside diff range and nitpick comments (12)
web/src/plugins/logs/Index.vue (7)
Line range hint
636-643: Correct Watcher Callback ParametersIn the
watchfunction forrouter.currentRoute.value.query.type, the callback parameters might be mismatched. Theprevparameter should correspond to the previous value ofrouter.currentRoute.value.query.type, not the entire previous route object.Update the callback parameters for clarity and correctness:
watch( () => router.currentRoute.value.query.type, - (type, prev) => { + (type, prevType) => { // Use prevType instead of prev if ( searchObj.shouldIgnoreWatcher == false && router.currentRoute.value.name === "logs" && - prev === "stream_explorer" && + prevType === "stream_explorer" && !type ) { searchObj.meta.pageType = "logs"; loadLogsData(); } }, );
Line range hint
650-656: Simplify Watcher Logic for Query ChangesThe watcher on
router.currentRoute.value.querycan be simplified by directly checking theactionproperty without usinghasOwnProperty.Refactor the watcher for better readability:
watch( () => router.currentRoute.value.query, () => { - if (!router.currentRoute.value.query.hasOwnProperty("action")) { + if (!router.currentRoute.value.query.action) { showSearchHistory.value = false; } - if ( - router.currentRoute.value.query.hasOwnProperty("action") && - router.currentRoute.value.query.action == "history" - ) { + if (router.currentRoute.value.query.action == "history") { showSearchHistory.value = true; } }, );
681-683: Maintain Consistent Code FormattingThere's an extra closing brace and parenthesis that may not be necessary. Ensure the watcher function is properly closed.
Double-check the closing of the watcher function to maintain code consistency.
778-778: Enhance Type Safety with Explicit TypesCurrently,
(item: any)is used in the map function, which can be improved for type safety.Specify a more precise type if possible:
const streamFieldNames: any = searchObj.data.stream.selectedStreamFields.map( - (item: any) => item.name, + (item: StreamField) => item.name, );Make sure to import or define the
StreamFieldinterface accordingly.
891-891: Update Function Signature DocumentationWith the addition of the
isFieldExistInSQLparameter insetInterestingFieldInSQLQuery, update the function's documentation to reflect this change.Add parameter details to the function comment:
/** * Adds or removes an interesting field from the SQL query. * @param {any} field - The field to add or remove. * @param {boolean} isFieldExistInSQL - Indicates if the field already exists in the SQL query. */ const setInterestingFieldInSQLQuery = (field: any, isFieldExistInSQL: boolean) => { /* ... */ }
962-965: Avoid Redundant Function CallsInside
handleQuickModeChange, callingsetQuery(searchObj.meta.quickMode);may be unnecessary ifsearchObj.meta.quickModehasn't changed.Verify if this function call is needed to prevent redundant operations.
Line range hint
1293-1309: Avoid Potential Race Conditions in Asynchronous WatcherIn the watcher for
showHistogram, the use of asynchronous functions likeawait this.generateHistogramSkeleton();inside a watcher might lead to unexpected behavior.Consider refactoring the code to manage asynchronous operations more predictably:
watch( () => this.searchObj.meta.showHistogram, async (newVal) => { if (newVal && !this.searchObj.shouldIgnoreWatcher) { // Existing logic } } );Alternatively, use
nextTickto ensure the DOM is updated before proceeding.web/src/composables/useLogs.ts (5)
Line range hint
2486-2504: Use 'forEach' instead of 'map' when not transforming arrayThe
.map()function is used here to iterate oversearchObj.data.queryResults.partitionDetail.partitionswithout returning any transformed array. Since the callback does not return a value and the result of.map()is not utilized, it's more appropriate to use.forEach()for side-effect operations.Apply this diff to fix the issue:
- searchObj.data.queryResults.partitionDetail.partitions.map( + searchObj.data.queryResults.partitionDetail.partitions.forEach( (item: any, index: any) => { if (item[0] == currentStartTime && item[1] == currentEndTime) { totalHits = res.data.hits.reduce( (accumulator: number, currentValue: any) => accumulator + Math.max(parseInt(currentValue.zo_sql_num, 10), 0), 0 ); searchObj.data.queryResults.partitionDetail.partitionTotal[ index ] = totalHits; return; } } );
Line range hint
2194-2226: Avoid reassigning a 'const' variableIn
generateHistogramSkeleton(), the variabledateis declared usingconstbut is reassigned within a loop. Reassigning aconstvariable will cause a runtime error. Declaredatewithletif it needs to be reassigned.Apply this diff to fix the issue:
- const date = new Date(); + let date = new Date();
Line range hint
2524-2548: Handle HTTP 429 errors in 'catch' blockIn the error handling for the histogram query, HTTP 429 (Too Many Requests) responses are not being handled. The current logic skips error handling when
err?.request?.status == 429, which might cause uncaught exceptions or user confusion when rate limits are reached. Consider adding appropriate handling for HTTP 429 errors.Apply this diff to handle HTTP 429 errors:
if (err?.request?.status != 429) { // Existing error handling ... } + else { + searchObj.data.histogram.errorMsg = err.response.data.error || 'Too Many Requests'; + notificationMsg.value = searchObj.data.histogram.errorMsg; + }
Line range hint
1822-1836: Possible infinite loop inrefreshPartitionPaginationIn the
refreshPartitionPaginationfunction, there is aforloop that iterates overpartitionDetail.partitions. Inside the loop, there is a conditionalreturn true;statement. Usingreturninside a loop might not behave as intended, as it will exit the entire function on the first iteration that meets the condition.Consider using
continueto skip to the next iteration, or restructure the logic to avoid premature function termination.
Line range hint
2096-2102: Unintentional assignment in conditional statementIn the conditional statement
if (searchObj.loadingHistogram == false && ...), using==for comparison is acceptable, but it is safer to use===to avoid type coercion issues.Consider using strict equality checks:
- if (searchObj.loadingHistogram == false && ...) + if (searchObj.loadingHistogram === false && ...)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
- web/src/composables/useLogs.ts (3 hunks)
- web/src/plugins/logs/Index.vue (23 hunks)
🧰 Additional context used
🔇 Additional comments (6)
web/src/plugins/logs/Index.vue (4)
312-318: Optimize the Import Statements Using Consistent Async ComponentsThe import statements for
IndexList,SearchResult, andConfirmDialoghave been updated to usedefineAsyncComponent. This enhances performance by lazy-loading these components.
1134-1136: Handle Timezone Consistently in Date CalculationsEnsure that
getConsumableRelativeTimeconsiders the correct timezone to avoid discrepancies in displayed data.Confirm that the function adjusts for timezone appropriately, especially if users are in different regions.
462-462: EnsuregenerateHistogramSkeletonis Properly IntegratedThe
generateHistogramSkeletonmethod is now being destructured fromuseLogs(). Verify that this method is correctly defined and exported inuseLogs()and that all its dependencies are satisfied.Run the following script to confirm the definition:
771-771: Verify the Third Parameter ingetStreamCallWhen calling
getStream, a third parametertrueis passed. Ensure that thegetStreamfunction accepts this parameter and that it's being used as intended.Check the
getStreamfunction's definition to confirm the parameter usage:web/src/composables/useLogs.ts (2)
Line range hint
1983-1984: Potential typo in variable nameThe variable
searchObj.data.queryResults.partitionDetail.partitionTotalis being updated, but in other parts of the code, the variable might be referred to assearchObj.data.queryResults.partitionDetail.partitionTotals(with an 's'). Ensure consistent naming to prevent undefined behavior.Check for inconsistent variable naming of
partitionTotalversuspartitionTotals.
4230-4230: Ensure all exported functions are definedThe function
reorderSelectedFieldsis being exported but its definition is not present in the provided code. This could lead to aReferenceErrorwhen the module is imported elsewhere.Run the following script to verify the existence of
reorderSelectedFields:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (4)
web/src/composables/useLogs.ts (4)
Line range hint
2063-2131: LGTM: Improved pagination and error handlingThe changes to
getPaginatedDataare well-implemented:
- The new
appendResultparameter allows for more flexible data handling, which could be useful for pagination or infinite scrolling.- The error handling has been improved to provide more detailed information, including trace IDs for debugging.
These enhancements will improve the function's usability and make troubleshooting easier.
Consider using a custom error object or a more structured error response to standardize error handling across the application. This could make it easier to process and display errors consistently throughout the UI.
Line range hint
2395-2409: LGTM: Enhanced error handling in histogram queryThe updates to
getHistogramQueryDataimprove error handling:
- More detailed error information is now provided, including trace IDs for better debugging.
- A specific check for HTTP status 429 (Too Many Requests) has been added, allowing for special handling of rate limiting scenarios.
These changes will make it easier to diagnose and handle errors in the histogram query process.
For consistency, consider extracting the error handling logic into a separate function that can be reused across different API calls in the application. This would ensure a uniform approach to error processing and reduce code duplication.
Line range hint
4042-4084: LGTM: Improved ORDER BY clause handlingThe refactoring of
addOrderByToQueryis a significant improvement:
- The addition of a try-catch block enhances error handling, making the function more robust.
- The logic for adding the ORDER BY clause has been updated to be more comprehensive, handling various scenarios more effectively.
These changes will make the function more reliable and easier to maintain.
Consider adding a comment explaining the purpose of the
includesTimestampcheck, as its usage is not immediately clear in the current implementation. This would improve code readability and make future maintenance easier.
Line range hint
1-4245: Overall improvements in error handling and functionalityThe changes made to this file represent a significant improvement in several areas:
- Enhanced error handling across multiple functions, providing more detailed information for debugging.
- Addition of new functions like
resetHistogramWithLimitErrorto handle specific scenarios.- Refactoring of existing functions to improve robustness and handle edge cases better.
- Consistent code style and structure maintained throughout the changes.
These improvements will lead to a more stable and maintainable codebase, as well as a better user experience due to more informative error messages and improved handling of various scenarios.
As the codebase grows, consider breaking down this large file into smaller, more focused modules. This could improve maintainability and make it easier for multiple developers to work on different parts of the code simultaneously. For example, you could separate histogram-related functions, pagination logic, and error handling into their own modules.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
- web/src/composables/useLogs.ts (75 hunks)
- web/src/plugins/logs/Index.vue (24 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- web/src/plugins/logs/Index.vue
🧰 Additional context used
🔇 Additional comments (1)
web/src/composables/useLogs.ts (1)
1801-1815: LGTM: New function to handle histogram reset for limit queriesThis new function
resetHistogramWithLimitErroris a good addition. It properly resets the histogram data and sets an appropriate error message when dealing with limit queries, which are not compatible with histogram generation.
b14d3d0 to
07680a8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (11)
web/src/composables/useLogs.ts (11)
1801-1814: LGTM! Consider adding a brief comment explaining the function's purpose.The
resetHistogramWithLimitErrorfunction is well-implemented and serves its purpose of resetting the histogram data structure when there's a limit error. It correctly clears existing data and sets appropriate error messages.Consider adding a brief comment above the function to explain its purpose and when it's called. This would improve code readability and maintainability.
Line range hint
1833-1893: Improved time calculations, but histogram data population is missing.The
generateHistogramSkeletonfunction has been significantly improved with more precise time calculations and better handling of different time intervals. However, there's a potential issue:
- The function no longer populates the
histogramResultsarray, which might affect the histogram generation in other parts of the code.Please review the commented-out code at the end of the function and ensure that the
histogramResultsarray is still being populated correctly. If the population logic has been moved elsewhere, make sure it's properly implemented to avoid any issues with histogram generation.
Line range hint
1955-1975: LGTM! Consider refactoring for code reuse.The
fnHistogramParsedSQLfunction is correctly implemented and effectively parses the provided SQL query while handling potential errors. It's very similar to thefnParsedSQLfunction, which is good for consistency.Consider refactoring these two functions to reduce code duplication. You could create a single helper function that both
fnParsedSQLandfnHistogramParsedSQLcall with their respective queries. This would improve maintainability and reduce the risk of inconsistencies if changes are needed in the future.
Line range hint
1977-2064: Improved error handling and tracing, but consider refactoring for maintainability.The
getPageCountfunction has been significantly improved:
- It now uses async/await syntax, which enhances readability.
- The addition of trace context generation is great for debugging.
- Error handling has been improved with more detailed error messages.
However, there are some areas for improvement:
- The function is quite long and complex. Consider breaking it down into smaller, more focused functions to improve maintainability.
- There's commented-out code that should be removed if it's no longer needed. This will improve code cleanliness.
- The error handling logic is repeated in several places. Consider extracting it into a separate function to reduce duplication.
Remove the commented-out code if it's no longer needed. If it's kept for future reference, add a comment explaining why it's being kept.
Line range hint
2077-2385: Improved error handling and flexibility, but needs refactoring for maintainability.The
getPaginatedDatafunction has been significantly enhanced:
- The use of async/await syntax improves readability.
- Trace context generation is great for debugging.
- Error handling has been improved with more detailed error messages.
- It handles various scenarios like SQL mode, multi-stream queries, and search around data effectively.
However, there are several areas for improvement:
- The function is extremely long and complex. Consider breaking it down into smaller, more focused functions to improve maintainability and testability.
- Extract the error handling logic into a separate function to reduce code duplication.
- Consider using the Strategy pattern or a similar design pattern to handle different scenarios (SQL mode, multi-stream, etc.) more cleanly.
Remove the commented-out code if it's no longer needed. If it's kept for future reference, add a comment explaining why it's being kept.
The function handles many different scenarios, which might make it prone to bugs and difficult to maintain. Consider splitting it into multiple functions, each handling a specific scenario.
Line range hint
2387-2411: LGTM! Consider usingmapfor a more idiomatic approach.The
filterHitsColumnsfunction correctly filters the hits columns based on selected fields and always includes the timestamp column. The logic is sound and works as expected.Consider refactoring the function to use
mapinstead of a forEach loop with push. This would make the code more idiomatic and potentially more efficient. Here's an example of how it could look:searchObj.data.queryResults.filteredHit = searchObj.data.queryResults.hits.map((hit) => { if (searchObj.data.stream.selectedFields.length > 0) { const filteredHit = {}; searchObj.data.stream.selectedFields.forEach((field) => { if (hit.hasOwnProperty(field)) { filteredHit[field] = hit[field]; } }); filteredHit[store.state.zoConfig.timestamp_column] = hit[store.state.zoConfig.timestamp_column]; return filteredHit; } return hit; });This approach creates a new array in a single operation, which is more in line with functional programming principles.
Line range hint
2413-2530: Improved cancellation handling and data processing, but needs refactoring.The
getHistogramQueryDatafunction has been significantly enhanced:
- The addition of operation cancellation handling is a great improvement.
- Error handling has been improved with more detailed error messages.
- The histogram data processing seems more efficient.
However, there are several areas for improvement:
- The function is quite long and complex. Consider breaking it down into smaller, more focused functions to improve maintainability.
- Extract the error handling logic into a separate function to reduce code duplication.
- Consider using a state machine or similar pattern to manage the different states of the histogram data retrieval process (cancelled, error, success) more cleanly.
Remove the commented-out code if it's no longer needed. If it's kept for future reference, add a comment explaining why it's being kept.
The error handling logic is repeated in several places. This increases the risk of inconsistencies if changes are needed in the future. Consider centralizing the error handling in a separate function.
Line range hint
2532-2563: LGTM! But check thereturnstatement in the inner loop.The
updateFieldValuesfunction correctly updates field values based on the query results, excluding specified fields and using a Set for efficient unique value storage.There's a potential issue with the
returnstatement inside the inner loop:if (excludedFields.includes(key)) { return; }This
returnwill only exit the inner loop, not the outer one. If the intention is to skip processing of excluded fields entirely, consider usingcontinueinstead ofreturn, or move this check outside the inner loop.Consider refactoring the nested loops to use
Array.prototype.reducefor a more functional approach. This could make the code more concise and potentially more efficient.
Line range hint
2565-3045: Improved schema handling, but requires major refactoring for maintainability.The
extractFieldsfunction has been significantly enhanced:
- It now handles user-defined schemas more robustly.
- The processing of multiple streams and common fields has been improved.
- Additional error handling and debugging information have been added.
However, there are critical issues that need to be addressed:
- The function is extremely long and complex (over 400 lines), making it very difficult to understand, maintain, and test.
- It handles many different scenarios within a single function, increasing the likelihood of bugs and making it hard to reason about the code's behavior.
A major refactoring is strongly recommended:
- Break down this function into smaller, more focused functions. Each function should handle a specific task (e.g., processing user-defined schemas, handling interesting fields, processing common fields).
- Consider using the Strategy pattern or a similar design pattern to handle different scenarios more cleanly.
- Extract repeated logic (like field processing) into separate helper functions.
- Use meaningful variable names to improve readability (e.g.,
UDSFieldCountcould beuserDefinedSchemaFieldCount).Remove commented-out code if it's no longer needed. If it's kept for future reference, add a comment explaining why it's being kept.
This refactoring will greatly improve the maintainability, readability, and testability of the code.
Line range hint
3047-3279: Improved column handling, but consider refactoring for clarity and maintainability.The
updateGridColumnsfunction has been enhanced:
- It now handles more scenarios, including SQL mode and selected fields.
- The column width calculation has been improved.
- Additional error handling has been added.
However, there are areas that could be improved:
- The function is quite long and handles multiple responsibilities. Consider breaking it down into smaller, more focused functions (e.g., one for timestamp column, one for selected fields, one for column width calculation).
- Extract the column width calculation logic into a separate function for better reusability and testability.
- Consider using a more declarative approach for column definition, possibly using a configuration object or factory function.
Remove commented-out code if it's no longer needed. If it's kept for future reference, add a comment explaining why it's being kept.
The nested loops and conditions make the function complex and harder to maintain. Consider simplifying the logic or breaking it down into smaller, more manageable pieces.
These changes will improve the readability, maintainability, and testability of the code.
Line range hint
3281-3315: LGTM! Consider optimizing canvas creation for better performance.The
getColumnWidthfunction effectively calculates the width of a column based on its content, using a canvas context to measure text width. It correctly handles different font styles for header and content and applies appropriate minimum and maximum constraints.To optimize performance, consider creating the canvas element once and reusing it across multiple calls to this function. You could do this by:
- Creating the canvas element at a higher scope (e.g., as a module-level constant).
- Passing the canvas context as an argument to this function.
This approach would avoid the overhead of creating a new canvas element for each column width calculation, which could be significant if this function is called frequently.
Example:
const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); function getColumnWidth(context, field) { // ... existing function body ... }This optimization could lead to improved performance, especially when dealing with tables with many columns.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
- web/src/components/iam/roles/EntityPermissionTable.vue (2 hunks)
- web/src/composables/useLogs.ts (75 hunks)
- web/src/plugins/logs/Index.vue (24 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- web/src/components/iam/roles/EntityPermissionTable.vue
- web/src/plugins/logs/Index.vue
🧰 Additional context used
🔇 Additional comments (5)
web/src/composables/useLogs.ts (5)
Line range hint
1816-1831: LGTM! TheisTimestampASCfunction is well-implemented.The function correctly checks if the timestamp column is ordered in ascending order. It properly iterates through the
orderbyarray and checks for the "ASC" type on the timestamp column.
Line range hint
1895-1906: LGTM! ThehasAggregationfunction is correctly implemented.The function effectively checks for the presence of aggregation functions in the SQL query by iterating through the columns array. It correctly returns true if an aggregation function is found and false otherwise.
Line range hint
1908-1931: LGTM! ThehasTimeStampColumnfunction is well-implemented.The function effectively checks for the presence of the timestamp column in the SQL query. It covers various scenarios, including direct column reference, wildcard selection, nested arguments, and aliased columns. The implementation is thorough and correct.
Line range hint
1933-1953: LGTM! ThefnParsedSQLfunction is correctly implemented.The function effectively parses the SQL query while handling potential errors:
- It filters out commented lines before parsing, which is a good practice.
- It uses a try-catch block to handle parsing errors gracefully.
- In case of an error, it returns a default structure, ensuring the code doesn't break.
Line range hint
2066-2075: LGTM! TheprocessPostPaginationDatafunction is well-structured.The function effectively processes data after pagination by calling the necessary functions in a logical order. It updates field values, extracts fields, updates grid columns, filters hits columns, and finally updates the histogram title. The implementation is concise and clear.
fix: reset histogram aggs and generate skeleton before generating histogram On enabling histogram, UI freezes. Go to logs, turn off histogram, select stream and click run-query after getting results, turn on histogram. fix: removed console log
07680a8 to
07195c1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Outside diff range and nitpick comments (9)
web/src/composables/useLogs.ts (7)
Line range hint
2395-2528: Consider refactoring getHistogramQueryData for improved maintainability.The changes to this function enhance error handling and data processing. However, there are several areas for improvement:
- The function is quite long and complex. Consider breaking it down into smaller, more focused functions.
- There's commented-out code that should be removed if no longer needed (e.g., lines 2436-2453).
- Error handling logic is repeated in multiple places. Consider extracting this into a separate function.
Here are some suggestions for refactoring:
- Extract the histogram data generation logic (lines 2408-2454) into a separate function.
- Create a reusable error handling function to reduce code duplication.
- Remove or update the commented-out code to improve readability.
Example of extracted error handling function:
function handleHistogramError(err: any) { searchObj.loadingHistogram = false; let trace_id = ""; searchObj.data.histogram.errorMsg = typeof err == "string" && err ? err : "Error while processing histogram request."; // ... rest of the error handling logic }This refactoring will improve the function's maintainability and readability.
Line range hint
2063-2391: Refactor getPaginatedData to improve maintainability and adhere to SOLID principles.While the changes enhance error handling and data processing, the function has become overly complex and difficult to maintain. Consider the following improvements:
- Break down the function into smaller, more focused functions adhering to the Single Responsibility Principle.
- Remove commented-out code if it's no longer needed (e.g., lines 2071-2079, 2280-2287).
- Extract repeated error handling logic into a separate function.
- Consider using a state machine or a more structured approach to handle the different stages of data fetching and processing.
Here's a suggested approach for refactoring:
- Extract the SQL mode and pagination handling into a separate function:
function handleSqlModeAndPagination(queryReq: any, parsedSQL: any) { // Logic from lines 2088-2117 }
- Create a reusable error handling function:
function handlePaginationError(err: any) { // Error handling logic }
- Extract the data processing logic into a separate function:
function processPaginatedData(res: any) { // Data processing logic from lines 2124-2292 }
- Refactor the main function to use these extracted functions:
const getPaginatedData = async (queryReq: any, appendResult: boolean = false) => { if (searchObj.data.isOperationCancelled) { handleCancelledOperation(); return; } handleSqlModeAndPagination(queryReq, fnParsedSQL()); try { const res = await searchService.search(/* ... */); await processPaginatedData(res); // ... rest of the success logic } catch (err) { handlePaginationError(err); } };This refactoring will significantly improve the function's readability, maintainability, and adherence to SOLID principles.
Line range hint
4042-4084: Improvements to addOrderByToQuery function, with suggestions for further enhancements.The changes to this function have improved the handling of ORDER BY clauses in SQL queries. However, there are some areas for further improvement:
- Error handling is minimal. Consider adding more detailed error logging or throwing a custom error.
- The function modifies the query even if an ORDER BY clause already exists. It might be more efficient to check for an existing ORDER BY clause first and only modify if necessary.
Here's a suggested refactor to address these points:
const addOrderByToQuery = (sql: string, column: string, type: "ASC" | "DESC", streamName: string) => { try { const parsedQuery: any = parser.astify(sql); if (!parsedQuery.columns.length || !parsedQuery.from) { return sql; } const hasOrderBy = !!(parsedQuery.orderby && parsedQuery.orderby.length > 0); const includesTimestamp = !!parsedQuery.columns.find( (col: any) => col?.expr?.column === column || col?.expr?.column === "*" ); if (!hasOrderBy && includesTimestamp) { parsedQuery.orderby = [{ expr: { type: "column_ref", table: null, column: column, }, type: type, }]; return quoteTableNameDirectly( parser.sqlify(parsedQuery).replace(/`/g, ""), streamName ); } return sql; // Return original SQL if no changes are needed } catch (err) { console.error("Error in addOrderByToQuery:", err); return sql; } };This refactored version only modifies the query if there's no existing ORDER BY clause and the timestamp column is included. It also adds basic error logging.
Line range hint
4099-4124: Improvements to getRegionInfo function, with suggestions for error handling and type safety.The changes to this function have improved the structure of region and cluster data. However, there are some areas for further enhancement:
- Error handling: The function doesn't handle potential errors from the API call.
- Type safety: There's no type checking or validation of the API response.
Here's a suggested refactor to address these points:
interface ClusterInfo { label: string; } interface RegionInfo { label: string; children: ClusterInfo[]; } const getRegionInfo = async (): Promise<void> => { try { const res = await searchService.get_regions(); const apiData: Record<string, string[]> = res.data; const clusterData: RegionInfo[] = Object.entries(apiData).map(([region, clusters]) => ({ label: region, children: clusters.map(cluster => ({ label: cluster })), })); store.dispatch("setRegionInfo", clusterData); } catch (error) { console.error("Error fetching region info:", error); // Consider showing a user-friendly error message // showErrorNotification("Failed to fetch region information"); } };This refactored version:
- Adds error handling for the API call.
- Uses TypeScript interfaces for better type safety.
- Simplifies the data transformation using
Object.entriesandmap.- Removes the unnecessary
regionObjvariable.These changes will make the function more robust and easier to maintain.
Line range hint
4135-4169: Enhancements to cancelQuery function with suggestions for improved error handling and return value.The changes to this function have improved the query cancellation process. However, there are some areas for further enhancement:
- Error handling could be more specific, potentially differentiating between different types of errors.
- The function doesn't return any value, making it difficult to programmatically determine if the cancellation was successful.
Here's a suggested refactor to address these points:
const cancelQuery = async (): Promise<boolean> => { const tracesIds = [...searchObj.data.searchRequestTraceIds]; searchObj.data.isOperationCancelled = true; try { const res = await searchService.delete_running_queries( store.state.selectedOrganization.identifier, searchObj.data.searchRequestTraceIds ); const isCancelled = res.data.some((item: any) => item.is_success); if (isCancelled) { searchObj.data.isOperationCancelled = false; $q.notify({ message: "Running query cancelled successfully", color: "positive", position: "bottom", timeout: 4000, }); } else { throw new Error("No queries were successfully cancelled"); } return isCancelled; } catch (error: any) { const errorMessage = error.response?.data?.message || "Failed to cancel running query"; $q.notify({ message: errorMessage, color: "negative", position: "bottom", timeout: 1500, }); return false; } finally { searchObj.data.searchRequestTraceIds = searchObj.data.searchRequestTraceIds.filter( (id: string) => !tracesIds.includes(id) ); } };This refactored version:
- Returns a boolean indicating whether the cancellation was successful.
- Uses async/await for better readability and error handling.
- Provides more specific error handling, including a custom error for when no queries were cancelled.
- Moves the trace ID cleanup to a
finallyblock to ensure it always happens.These changes will make the function more robust and easier to use programmatically.
Line range hint
4171-4183: Enhance reorderArrayByReference for clarity and predictability.The new
reorderArrayByReferencefunction correctly reorders an array based on a reference array. However, consider the following improvements:
- The function modifies the input array in-place, which might lead to unexpected side effects. Consider creating a new array instead.
- The type annotations could be more specific to improve type safety.
- The function name could be more descriptive to better convey its purpose.
Here's a suggested refactor:
function sortArrayByReferenceOrder<T>(arrayToSort: T[], referenceArray: T[]): T[] { return [...arrayToSort].sort((a, b) => { const indexA = referenceArray.indexOf(a); const indexB = referenceArray.indexOf(b); if (indexA === -1 && indexB === -1) return 0; if (indexA === -1) return 1; if (indexB === -1) return -1; return indexA - indexB; }); }This refactored version:
- Creates a new sorted array instead of modifying the input array.
- Uses generic type
Tfor better type safety.- Has a more descriptive name
sortArrayByReferenceOrder.- Handles the case where both elements are not in the reference array.
Usage example:
const sortedArray = sortArrayByReferenceOrder(arr1, arr2);This approach provides a clearer and more predictable behavior while maintaining the original functionality.
Line range hint
4185-4202: Optimize reorderSelectedFields for efficiency and robustness.The new
reorderSelectedFieldsfunction correctly reorders the selected fields based on a column order. However, consider the following improvements:
- Avoid unnecessary array copying and comparison for better efficiency.
- Add more specific type annotations to improve type safety.
- Handle the case where
colOrderis undefined more gracefully.Here's a suggested refactor:
function reorderSelectedFields(): string[] { const selectedFields = searchObj.data.stream.selectedFields; const streamKey = searchObj.data.stream.selectedStream; let colOrder = searchObj.data.resultGrid.colOrder[streamKey]; if (!Array.isArray(colOrder)) { return selectedFields; // Return original array if colOrder is not valid } const timestampColumn = store.state.zoConfig.timestamp_column; const includesTimestamp = selectedFields.includes(timestampColumn); if (!includesTimestamp) { colOrder = colOrder.filter(v => v !== timestampColumn); } // Only reorder if necessary if (!arraysEqual(selectedFields, colOrder)) { return sortArrayByReferenceOrder(selectedFields, colOrder); } return selectedFields; } // Helper function to check if arrays are equal function arraysEqual(a: string[], b: string[]): boolean { return a.length === b.length && a.every((v, i) => v === b[i]); }This refactored version:
- Avoids unnecessary array copying by only creating a new array if reordering is needed.
- Uses more specific type annotations.
- Handles the case where
colOrderis undefined or not an array.- Uses a helper function
arraysEqualto efficiently check if reordering is necessary.- Assumes the
sortArrayByReferenceOrderfunction from the previous refactoring suggestion is used.These changes will make the function more efficient and robust while maintaining its original functionality.
web/src/plugins/logs/Index.vue (2)
Line range hint
652-659: Simplify Conditional Logic in WatcherThe watcher for
router.currentRoute.value.querycan be simplified for better readability and efficiency.Consider simplifying the watcher as follows:
watch( () => router.currentRoute.value.query, () => { - if (!router.currentRoute.value.query.hasOwnProperty("action")) { - showSearchHistory.value = false; - } - if ( - router.currentRoute.value.query.hasOwnProperty("action") && - router.currentRoute.value.query.action == "history" - ) { - showSearchHistory.value = true; - } + showSearchHistory.value = + router.currentRoute.value.query.action === "history"; }, );
Line range hint
1299-1335: Avoid Async Watcher FunctionsUsing
asyncin a watcher function can lead to unexpected behavior because Vue does not wait for promises in watchers. This might cause race conditions or unhandled promise rejections.Refactor the watcher to handle asynchronous operations without making the entire watcher function
async:-watch( +watchEffect( async () => { - async showHistogram() { let parsedSQL = null; if (this.searchObj.meta.sqlMode) parsedSQL = this.fnParsedSQL(); if ( this.searchObj.meta.showHistogram && !this.searchObj.shouldIgnoreWatcher ) { this.searchObj.data.queryResults.aggs = []; if ( this.searchObj.meta.sqlMode && !this.isNonAggregatedQuery(parsedSQL) ) { this.resetHistogramWithLimitError(); } else if (this.searchObj.meta.histogramDirtyFlag == true) { this.searchObj.meta.histogramDirtyFlag = false; // this.handleRunQuery(); this.searchObj.loadingHistogram = true; - await this.generateHistogramSkeleton(); + // Handle asynchronous operations inside the watcher + (async () => { + await this.generateHistogramSkeleton(); - this.getHistogramQueryData(this.searchObj.data.histogramQuery) - .then((res: any) => { - this.refreshTimezone(); - this.searchResultRef.reDrawChart(); - }) - .catch((err: any) => { - console.log(err, "err in updating chart"); - }) - .finally(() => { - this.searchObj.loadingHistogram = false; - }); + try { + await this.getHistogramQueryData(this.searchObj.data.histogramQuery); + this.refreshTimezone(); + this.searchResultRef.reDrawChart(); + } catch (err) { + console.log(err, "err in updating chart"); + } finally { + this.searchObj.loadingHistogram = false; + } + })(); setTimeout(() => { if (this.searchResultRef) this.searchResultRef.reDrawChart(); }, 100); } } this.updateUrlQueryParams(); - }, }, );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
- web/src/components/iam/roles/EntityPermissionTable.vue (2 hunks)
- web/src/composables/useLogs.ts (75 hunks)
- web/src/plugins/logs/Index.vue (24 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- web/src/components/iam/roles/EntityPermissionTable.vue
🧰 Additional context used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (6)
web/src/composables/useLogs.ts (3)
217-221: Approve TODO for code organization refactoring.The TODO comment outlines a good plan for improving code organization by separating concerns into distinct composables. This aligns well with best practices for maintainability and modularity.
Would you like me to create a GitHub issue to track this refactoring task? This will help ensure it's not forgotten and can be prioritized appropriately.
4248-4249: Approve isNonAggregatedQuery function with a suggestion for enhancement.The
isNonAggregatedQueryfunction is implemented correctly and handles the case whereparsedSQLmight be null. Its name clearly describes its purpose, which is good for readability and maintainability.Consider if additional checks might be needed for a more comprehensive determination of aggregated queries. For example, you might want to check for the presence of aggregate functions (COUNT, SUM, AVG, etc.) or GROUP BY clauses. This could make the function more robust:
function isNonAggregatedQuery(parsedSQL: any = null): boolean { if (!parsedSQL) return true; const hasLimit = !!parsedSQL.limit; const hasGroupBy = !!parsedSQL.groupby; const hasAggregateFunctions = parsedSQL.columns?.some((col: any) => col.expr && col.expr.type === 'aggr_func' ); return !hasLimit && !hasGroupBy && !hasAggregateFunctions; }This enhanced version checks for LIMIT, GROUP BY, and aggregate functions, providing a more comprehensive determination of non-aggregated queries.
Line range hint
4049-4087: Approve addOrderByToQuery function with suggestions for improvement.The
addOrderByToQueryfunction effectively adds an ORDER BY clause to a SQL query when it doesn't exist. It handles parsing errors and checks for the presence of the timestamp column in the SELECT clause, which are good practices.Consider the following improvements to make the function more robust:
Handle existing ORDER BY clauses more gracefully:
- If the specified column is already in the ORDER BY clause, update its sort order instead of adding a new clause.
- If other columns are in the ORDER BY clause, consider whether to preserve them or replace the entire clause.
Improve handling of complex queries:
- Add support for queries with subqueries or Common Table Expressions (CTEs).
- Consider using a more robust SQL parser that can handle a wider range of SQL syntax.
Here's a sketch of how you might implement these improvements:
function addOrderByToQuery(sql: string, column: string, type: "ASC" | "DESC", streamName: string) { try { const parsedQuery: any = parser.astify(sql); if (!parsedQuery.columns.length || !parsedQuery.from) { return sql; } const existingOrderBy = parsedQuery.orderby || []; const columnIndex = existingOrderBy.findIndex((order: any) => order.expr.column === column); if (columnIndex !== -1) { // Update existing ORDER BY for the column existingOrderBy[columnIndex].type = type; } else { // Add new ORDER BY existingOrderBy.push({ expr: { type: "column_ref", table: null, column: column }, type: type }); } parsedQuery.orderby = existingOrderBy; // Handle subqueries and CTEs here if needed return quoteTableNameDirectly(parser.sqlify(parsedQuery).replace(/`/g, ""), streamName); } catch (err) { console.error("Error modifying ORDER BY clause:", err); return sql; } }This improved version updates existing ORDER BY clauses for the specified column and preserves other ordered columns. You may need to add additional logic to handle subqueries and CTEs based on your specific use cases.
web/src/plugins/logs/Index.vue (3)
Line range hint
637-651: Use Strict Equality for Boolean ComparisonsIn the watcher, it's better to use strict equality (
===) when comparing boolean values to avoid unexpected type coercion.- if (searchObj.shouldIgnoreWatcher == false && ...) + if (searchObj.shouldIgnoreWatcher === false && ...)
772-772: Clarify the Purpose of thetrueArgumentWhen calling
getStream, the purpose of thetrueargument is not immediately clear. Adding a comment or using a named parameter can improve code readability.// The third argument 'true' forces a cache refresh const streamData = getStream( searchObj.data.stream.selectedStream[0], searchObj.data.stream.streamType || "logs", true // Force cache refresh );
Line range hint
1297-1336: Handle Asynchronous Operations Carefully in WatcherIn your watcher for
showHistogram, ensure that asynchronous operations are properly handled to prevent race conditions or memory leaks.
- Use try-catch blocks around
awaitcalls.- Cancel any pending operations if the watcher is triggered again before completion.
watch( () => this.showHistogram, async () => { try { // Your existing logic } catch (error) { console.error("Error updating histogram:", error); } } );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
- web/src/composables/useLogs.ts (76 hunks)
- web/src/plugins/logs/Index.vue (24 hunks)
🧰 Additional context used
🔇 Additional comments (3)
web/src/composables/useLogs.ts (1)
1808-1821: Approve implementation of resetHistogramWithError function.The new
resetHistogramWithErrorfunction effectively resets the histogram data structure with the provided error information. It aligns well with the previous refactoring suggestion, improving code reusability and flexibility in error handling for the histogram.The implementation correctly uses the
getHistogramTitle()function for consistency and allows for both error message and error code to be set, which enhances error reporting capabilities.web/src/plugins/logs/Index.vue (2)
1066-1069: Verify Index Access SafetyAlthough you're checking
fields.x.length == 2before accessingfields.x[1], ensure thatfields.xalways has the expected structure, especially if there's any possibility of asynchronous mutations.
963-963:⚠️ Potential issueEnsure Robust Regex Replacement
The regular expression used in
searchObj.data.query.replacemay unintentionally replace text if there are multipleSELECT...FROMclauses or comments in the query.Consider updating the regex to be more precise:
- /SELECT\s+(.*?)\s+FROM/i + /^SELECT\s+.*?\s+FROM\b/iLikely invalid or redundant comment.
| () => import("@/plugins/logs/IndexList.vue"), | ||
| ), | ||
| SearchResult: defineAsyncComponent( | ||
| () => import("@/plugins/logs/SearchResult.vue") | ||
| () => import("@/plugins/logs/SearchResult.vue"), | ||
| ), | ||
| ConfirmDialog: defineAsyncComponent( | ||
| () => import("@/components/ConfirmDialog.vue") | ||
| () => import("@/components/ConfirmDialog.vue"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Ensure Consistent Component Import Strategies
Currently, IndexList, SearchResult, and ConfirmDialog are imported asynchronously using defineAsyncComponent, while other components like SearchBar and SearchHistory are imported synchronously. For consistency and potential performance improvements, consider importing all non-critical components asynchronously.
| JSON.stringify(dashboardPanelData.data), | ||
| ); | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use Efficient Deep Cloning Method
Using JSON.parse(JSON.stringify(...)) for deep cloning is inefficient and can cause issues with functions or complex data types.
Consider using structuredClone or a utility function:
visualizeChartData.value = structuredClone(dashboardPanelData.data);Or with lodash:
import { cloneDeep } from "lodash-es";
visualizeChartData.value = cloneDeep(dashboardPanelData.data);
#4806
Issue replication steps:
Go to logs, turn off histogram, select stream and click run-query after getting results, turn on histogram.
Summary by CodeRabbit
New Features
Improvements
Minor Adjustments