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

Pull request update/240405 #251

Merged
merged 4 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions insider/insider_worker/http_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ def retry_if_connection_error(exception):
if isinstance(exception, requests.HTTPError):
if exception.response.status_code in (503,):
return True
# retry too many requests
elif exception.response.status_code in (429,):
return True
return False


Expand Down
23 changes: 18 additions & 5 deletions metroculus/metroculus_worker/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,12 +537,14 @@ def get_gcp_metrics(cloud_account_id, cloud_resource_ids, resource_ids_map,
"cpu": "compute.googleapis.com/instance/cpu/utilization",
"network_in_io": "compute.googleapis.com/instance/network/received_bytes_count",
"network_out_io": "compute.googleapis.com/instance/network/sent_bytes_count",
"ram_percent": "agent.googleapis.com/memory/percent_used",
"ram_size": "compute.googleapis.com/instance/memory/balloon/ram_size",
"ram": "compute.googleapis.com/instance/memory/balloon/ram_used",
"disk_read_io": "compute.googleapis.com/instance/disk/read_ops_count",
"disk_write_io": "compute.googleapis.com/instance/disk/write_ops_count",
}
ram_sizes = {}
ram_percents = set()
for metric_name, cloud_metric_name in metric_cloud_names_map.items():
response = adapter.get_metric(
cloud_metric_name,
Expand Down Expand Up @@ -579,17 +581,28 @@ def get_gcp_metrics(cloud_account_id, cloud_resource_ids, resource_ids_map,
'disk_write_io']:
# change values per min to values per second
value = value / 60
# RAM value in % is returned on instances with Ops agent
elif metric_name == "ram_percent":
if record.metric.labels.get('state') != 'used':
continue
key = (resource_id, date)
ram_percents.add(key)
# to determine RAM value in % instead of absolute values,
# we need to know values of 2 metrics - ram_used and ram_size - for the same time
# so we store ram_size in a map and pop values from it later when processing ram_used.
# we rely on the fact that the metrics API returns metrics in the same order
# as they were requested.
# on instances without Ops agent, we need to know values
# of 2 metrics - ram_used and ram_size - for the same time
# so we store ram_size in a map and pop values from it
# later when processing ram_used.
# we rely on the fact that the metrics API returns metrics
# in the same order as they were requested.
elif metric_name == "ram_size":
key = (resource_id, date)
ram_sizes[key] = value
continue
elif metric_name == "ram":
key = (resource_id, date)
if key in ram_percents:
# not calculate ram value, as agent's value exists
continue
ram_size = ram_sizes.pop(key, None)
if ram_size is None:
LOG.warn(
Expand All @@ -609,7 +622,7 @@ def get_gcp_metrics(cloud_account_id, cloud_resource_ids, resource_ids_map,
'cloud_account_id': cloud_account_id,
'resource_id': resource_id,
'date': date,
'metric': metric_name,
'metric': 'ram' if 'ram' in metric_name else metric_name,
'value': value
})
return result
Expand Down
43 changes: 43 additions & 0 deletions ngui/ui/src/components/ApolloApiErrorAlert/ApolloApiErrorAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useState } from "react";
import { useQuery } from "@apollo/client";
import ApiErrorMessage from "components/ApiErrorMessage";
import SnackbarAlert from "components/SnackbarAlert";
import { GET_ERROR } from "graphql/api/common";

// TODO: implement ERROR_HANDLER_TYPE_ALERT analogy for Apollo queries. https://www.apollographql.com/docs/react/v2/data/error-handling/
const ApolloApiErrorAlert = () => {
const { data = {} } = useQuery(GET_ERROR);

const { error: { error_code: errorCode, reason: errorReason, url, params } = {} } = data;

const [open, setOpen] = useState(false);

useEffect(() => {
setOpen(!!errorCode);
}, [errorCode]);

const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};

const errorMessage = errorCode && <ApiErrorMessage errorCode={errorCode} reason={errorReason} url={url} params={params} />;

return (
errorMessage !== null && (
<SnackbarAlert
severity="error"
body={errorMessage}
openState={open}
handleClose={handleClose}
dataTestIds={{
snackbar: "alert_error"
}}
/>
)
);
};

export default ApolloApiErrorAlert;
3 changes: 3 additions & 0 deletions ngui/ui/src/components/ApolloApiErrorAlert/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ApolloApiErrorAlert from "./ApolloApiErrorAlert";

export default ApolloApiErrorAlert;
38 changes: 31 additions & 7 deletions ngui/ui/src/components/ApolloProvider/ApolloProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { ApolloClient, ApolloProvider, InMemoryCache, split, HttpLink } from "@apollo/client";
import { ApolloClient, ApolloProvider, InMemoryCache, split, HttpLink, from, type DefaultContext } from "@apollo/client";
import { onError, type ErrorResponse } from "@apollo/client/link/error";
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { getMainDefinition } from "@apollo/client/utilities";
import { type GraphQLError } from "graphql";
import { createClient } from "graphql-ws";
import { GET_TOKEN } from "api/auth/actionTypes";
import { GET_ERROR } from "graphql/api/common";
import { useApiData } from "hooks/useApiData";
import { getEnvironmentVariable } from "utils/env";

const httpBase = getEnvironmentVariable("VITE_APOLLO_HTTP_BASE");
const wsBase = getEnvironmentVariable("VITE_APOLLO_WS_BASE");

const writeErrorToCache = (cache: DefaultContext, graphQLError: GraphQLError) => {
const { extensions: { response: { url, body: { error } = {} } = {} } = {} } = graphQLError;

cache.writeQuery({
query: GET_ERROR,
data: { error: { __typename: "Error", ...error, url } }
});
};

const ApolloClientProvider = ({ children }) => {
const {
apiData: { token }
Expand All @@ -27,11 +39,23 @@ const ApolloClientProvider = ({ children }) => {
})
);

/*
@param A function that's called for each operation to execute
@param The Link to use for an operation if the function returns a "truthy" value
@param The Link to use for an operation if the function returns a "falsy" value
*/
const errorLink = onError(({ graphQLErrors, networkError, operation }: ErrorResponse) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, path }) => console.log(`[GraphQL error]: Message: ${message}, Path: ${path}`));

const { cache } = operation.getContext();
writeErrorToCache(cache, graphQLErrors[0]);
}

/* Just log network errors for now.
We rely on custom error codes that are returned in graphQLErrors.
It might be usefult to cache networkError errors to display alerts as well.
*/
if (networkError) {
console.error(`[Network error]: ${networkError}`);
}
});

const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
Expand All @@ -42,7 +66,7 @@ const ApolloClientProvider = ({ children }) => {
);

const client = new ApolloClient({
link: splitLink,
link: from([errorLink, splitLink]),
cache: new InMemoryCache()
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SlicedText from "components/SlicedText";
import { ModelVersion } from "services/MlModelsService";
import { DEFAULT_MAX_INPUT_LENGTH } from "utils/constants";
import { SPACING_1 } from "utils/layouts";
import { notOnlyWhiteSpaces } from "utils/validation";

export const FIELD_NAME = "aliases";

Expand Down Expand Up @@ -54,7 +55,13 @@ const ConflictingAliasesWarning = ({ modelVersion, aliasesFieldName, aliasToVers
));
};

export const isAliasValid = (alias: string) => alias.length <= DEFAULT_MAX_INPUT_LENGTH;
export const isAliasValid = (alias: string) => {
const isLengthValid = alias.length <= DEFAULT_MAX_INPUT_LENGTH;

const containsNotOnlyWhiteSpaces = notOnlyWhiteSpaces(alias) === true;

return isLengthValid && containsNotOnlyWhiteSpaces;
};

const EditModelVersionAliasFormAliasesField = ({
name = FIELD_NAME,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import IconButton from "components/IconButton";
import Input from "components/Input";
import { DEFAULT_MAX_INPUT_LENGTH } from "utils/constants";
import { SPACING_1 } from "utils/layouts";
import { notOnlyWhiteSpaces } from "utils/validation";

export const ARRAY_FIELD_NAME = "tags";
export const KEY_FIELD_NAME = "key";
Expand Down Expand Up @@ -52,7 +53,8 @@ const KeyInput = ({ index }: { index: number }) => {
const isPropertyUnique = tagsWithSameKey.length === 1;

return isPropertyUnique || intl.formatMessage({ id: "thisFieldShouldBeUnique" });
}
},
notOnlyWhiteSpaces
}
})}
dataTestId={`tag_name_${index}`}
Expand Down Expand Up @@ -88,6 +90,9 @@ const ValueInput = ({ index }: { index: number }) => {
max: DEFAULT_MAX_INPUT_LENGTH
}
)
},
validate: {
notOnlyWhiteSpaces
}
}}
render={({ field }) => (
Expand Down
2 changes: 2 additions & 0 deletions ngui/ui/src/components/MlModel/MlModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const Version = ({ versions = [], isLoading = false }: VersionProps) => {
</TextWithDataTestId>
),
id: "aliases",
enableSorting: false,
accessorFn: ({ aliases }) => aliases.join(", "),
cell: ({ row: { original } }) => {
const { aliases } = original;
Expand Down Expand Up @@ -201,6 +202,7 @@ const Version = ({ versions = [], isLoading = false }: VersionProps) => {
style: {
minWidth: "200px"
},
enableSorting: false,
accessorFn: (originalRow) =>
Object.entries(originalRow.tags ?? {})
.map(([key, val]) => `${key}: ${val}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import Input from "components/Input";
import InputLoader from "components/InputLoader";
import { DEFAULT_MAX_TEXTAREA_LENGTH } from "utils/constants";
import { notOnlyWhiteSpaces } from "utils/validation";

export const FIELD_NAME = "description";

Expand Down Expand Up @@ -33,6 +34,9 @@ const MlModelFormDescriptionField = ({ name = FIELD_NAME, isLoading = false }) =
{ id: "maxLength" },
{ inputName: intl.formatMessage({ id: "description" }), max: DEFAULT_MAX_TEXTAREA_LENGTH }
)
},
validate: {
notOnlyWhiteSpaces
}
})}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import Input from "components/Input";
import InputLoader from "components/InputLoader";
import { DEFAULT_MAX_INPUT_LENGTH } from "utils/constants";
import { notOnlyWhiteSpaces } from "utils/validation";

export const FIELD_NAME = "key";

Expand Down Expand Up @@ -34,6 +35,9 @@ const MlModelFormKeyField = ({ name = FIELD_NAME, isLoading = false }) => {
{ id: "maxLength" },
{ inputName: intl.formatMessage({ id: "key" }), max: DEFAULT_MAX_INPUT_LENGTH }
)
},
validate: {
notOnlyWhiteSpaces
}
})}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import Input from "components/Input";
import InputLoader from "components/InputLoader";
import { DEFAULT_MAX_INPUT_LENGTH } from "utils/constants";
import { notOnlyWhiteSpaces } from "utils/validation";

export const FIELD_NAME = "name";

Expand Down Expand Up @@ -35,6 +36,9 @@ const MlModelFormNameField = ({ name = FIELD_NAME, isLoading = false }) => {
{ id: "maxLength" },
{ inputName: intl.formatMessage({ id: "name" }), max: DEFAULT_MAX_INPUT_LENGTH }
)
},
validate: {
notOnlyWhiteSpaces
}
})}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Input from "components/Input";
import InputLoader from "components/InputLoader";
import { DEFAULT_MAX_INPUT_LENGTH } from "utils/constants";
import { SPACING_1 } from "utils/layouts";
import { notOnlyWhiteSpaces } from "utils/validation";

export const ARRAY_FIELD_NAME = "tags";
export const KEY_FIELD_NAME = "key";
Expand Down Expand Up @@ -52,7 +53,8 @@ const KeyInput = ({ index }: { index: number }) => {
const isPropertyUnique = tagsWithSameKey.length === 1;

return isPropertyUnique || intl.formatMessage({ id: "thisFieldShouldBeUnique" });
}
},
notOnlyWhiteSpaces
}
})}
dataTestId={`tag_name_${index}`}
Expand Down Expand Up @@ -88,6 +90,9 @@ const ValueInput = ({ index }: { index: number }) => {
max: DEFAULT_MAX_INPUT_LENGTH
}
)
},
validate: {
notOnlyWhiteSpaces
}
}}
render={({ field }) => (
Expand Down
9 changes: 7 additions & 2 deletions ngui/ui/src/components/MlModels/MlModels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,13 @@ const ModelsTable = ({ models }: ModelsTableProps) => {
<FormattedMessage id="description" />
</TextWithDataTestId>
),
accessorKey: "description",
cell: ({ cell }) => <Markdown>{cell.getValue()}</Markdown>
id: "description",
accessorFn: (originalRow) => originalRow.description ?? "",
cell: ({ cell }) => {
const description = cell.getValue();

return description ? <Markdown>{description}</Markdown> : CELL_EMPTY_VALUE;
}
},
tags({
id: "tags",
Expand Down
9 changes: 9 additions & 0 deletions ngui/ui/src/graphql/api/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { gql } from "@apollo/client";

const GET_ERROR = gql`
query GetError {
error @client
}
`;

export { GET_ERROR };
2 changes: 2 additions & 0 deletions ngui/ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { PersistGate } from "redux-persist/integration/react";
import ActivityListener from "components/ActivityListener";
import ApiErrorAlert from "components/ApiErrorAlert";
import ApiSuccessAlert from "components/ApiSuccessAlert";
import ApolloApiErrorAlert from "components/ApolloApiErrorAlert";
import ApolloProvider from "components/ApolloProvider";
import App from "components/App";
import SideModalManager from "components/SideModalManager";
Expand Down Expand Up @@ -42,6 +43,7 @@ root.render(
<App />
</SideModalManager>
<Tour />
<ApolloApiErrorAlert />
<ApiErrorAlert />
<ApiSuccessAlert />
</ThemeProviderWrapper>
Expand Down
Loading