Skip to content
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

Monitor query updates #17459

Merged
merged 58 commits into from
Sep 9, 2021
Merged

Conversation

KarishmaGhiya
Copy link
Member

@KarishmaGhiya KarishmaGhiya commented Sep 3, 2021

Addresses #15988 and #17066

@ghost ghost added the Monitor Monitor, Monitor Ingestion, Monitor Query label Sep 3, 2021
@KarishmaGhiya KarishmaGhiya requested review from chradek, xirzec and scottaddie and removed request for richardpark-msft September 4, 2021 09:03
@@ -66,15 +65,19 @@ export interface LogsColumn {
export type LogsColumnType = string;

// @public
export type LogsQueryBatchOptions = OperationOptions;
export interface LogsQueryBatchOptions extends OperationOptions {
throwOnAnyFailure?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we return a promise that can be rejected, I think rejectOnAnyFailure might be more accurate.

But remind me...so if I set this to true, then if any of the queries in the batch fails, the whole thing fails? The scenario you talked to me about before was disabling throwing an error on partial failures, so I'm curious about this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - this one will throw for both complete and partial failures. And the whole thing will fail in this case. But the users will get the data about all the responses. We wanted to follow similar approach across all JS sdks -

if (options.throwOnAnyFailure && result._response.status === 207) {
Search-documents uses that flag - so thought of using the same approach.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah gotcha, if search-documents already uses that flag, then disregard my suggestion. Rather keep it consistent with throwOnAnyFailure then.

while (!result.done) {
console.log(` metricDefinitions - ${result.value.id}, ${result.value.name}`);
secondMetricName = result.value.name!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use for await(const result of iterator) instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also...this has the potential to return lots of results right? Otherwise you'd exit the loop as soon as you got the 2nd metric. Can you collect all the names to pass to the query method?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we can collect all the names and pass it to query method. Should I change the sample to that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking of letting this be - and may be adding a comment that they can pass in all the names to the query method. What do you think? @chradek

return convertResponseForQueryBatch(flatResponse, rawResponse);
const result: LogsQueryBatchResult = convertResponseForQueryBatch(flatResponse, rawResponse);
if (options?.throwOnAnyFailure && result.batchResultStatus !== "AllSucceeded") {
throw result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

result.logsQueryResultStatus = "Success";
}
if (options?.throwOnAnyFailure && result.logsQueryResultStatus !== "Success") {
throw result;
Copy link
Member

@maorleger maorleger Sep 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm I'm not sure if throwing a result object is the right thing here - we should throw something that is an Error object

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - we are just following what search documents is doing - in case if i throw an error - then the user won't get the data for queries that succeeded.

Copy link
Member

@maorleger maorleger Sep 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at the guidelines on error handling and while it's not definitively stated that we should always use an ErrorLike I can think of a few reasons why we should be careful with this approach:

  1. I expect an error to have a name and message property (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message)
  2. I am writing a UI app and I just put error.toString() in a banner - what would that look like? [object Object]?
  • I guess it's better if I use promise API but even then, catch expects rejected with a reason as the argument...

Not sure, I think it feels weird to throw something that is not an error. @bterlson or @chradek might convince me otherwise and maybe I have it all wrong in which case it'll be a good chance to learn what is right 😄

Edit to add: maybe I'm just not thinking about it right because it's a partial failure / partial success scenario...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I agree we should have a name, code, and message property (i.e. be error-like), otherwise error handling becomes awkward. Additional properties are fine if needed, though should be used sparingly since TS tooling doesn't help with them without the user explicitly casting.

It's unfortunate that search-documents isn't this way, probably in part because thrown errors don't show up in the API review 😕 I wonder what motivated the search-documents design, does anyone know?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to ask @xirzec but I wouldn't be surprised if it was an oversight. It wouldn't have shown up in the API review file and the PR that added it was pretty massive (#7482)

I do also think we should throw something error-like. I suspect users would expect message to be available at the very least.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about the complete reasoning for search-documents, but for monitor-query and search-documents - one thing is true that we have multiple results being returned in that one response. For monitor query, in a single response we have multiple result objects and each of them has an error property that has the structure - similar to the guidelines - https://apiview.dev/Assemblies/Review/06691b2d79d543a58a95ad4d0ea22ed4#@azure/monitor-query!ErrorInfo:interface

We need to figure out what to do when user sets the "throwOnAnyError" flag -

  • Should we throw the first error?
  • Should we throw a list of all errors
  • In cases of partial errors (i.e, for responses that have both table property and error property), should we throw both table and error property ?
  • Other languages (.NET and Java) are throwing the errors on cases of complete errors and are throwing on partial errors if that flag is set.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com>
Comment on lines +265 to +267
getMetricByName(metricName) {
return this.metrics.find((it) => it.name === metricName);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could optimize this by defining the function getMetricByName outside of this function, and just assigned it to the MetricsQueryResult object.

Currently, you're creating a new function for every metric result. Changing how this is built would allow you to re-use a single function.

Rough example:

function getMetricByName(metricName: string) {
  return this.metrics.find((it) => it.name === metricName);
}

function createMetricsQueryResult(...params): MetricsQueryResult {
  return { ...params, getMetricByName };
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} catch (e) {
return {
visualization: response.body?.render,
status: "Success",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An error occurred, but the status is Success?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am updating it in the conditions later. I couldn't put a complex logic in this one line

Comment on lines 124 to 130
if (result.tables && result.error) {
result.status = "Partial";
} else if (result.error && !result.tables) {
result.status = "Failed";
} else {
result.status = "Success";
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's interesting. In a different part of your code, you have Failed as the fall-through case, but here it's success. Any reason for the difference?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason really. But now I am thinking if i should refine this - since the tables is never optional in the single query response.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 if (!result.error) {
   // if there is no error field, it is success
   result.status = "Success";
 } else {
   // result.tables is always present in single query response, even is there is error
   if (result.tables.length === 0) {
     result.status = "Failed";
   } else {
     result.status = "Partial";
   }
 }
 ```

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this? So in the other batch query response - the tables can be undefined, but in single query response the "tables" field is always returned. So I think the above code snippet in the comment seems more appropriate for single query.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chradek wanted follow up on this

Co-authored-by: chradek <51000525+chradek@users.noreply.github.com>
Copy link
Member

@maorleger maorleger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 a few nits but I'll leave up to you! We should make an issue for addressing the throw result before GA'ing this library

status?: number;
/** The list of tables, columns and rows. */
// (hoisted up from `LogQueryResult`)
results: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just LogsQueryResult[] or Array<LogsQueryResult>? Maybe we can reuse the type instead of duplicating if so

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KarishmaGhiya KarishmaGhiya enabled auto-merge (squash) September 9, 2021 22:23
KarishmaGhiya and others added 5 commits September 9, 2021 16:08
Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com>
Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com>
Co-authored-by: Scott Addie <10702007+scottaddie@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Monitor Monitor, Monitor Ingestion, Monitor Query
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants