-
Notifications
You must be signed in to change notification settings - Fork 59
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
feat: Autoscaler #1077
feat: Autoscaler #1077
Changes from all commits
c0630df
b1b898d
ea8860c
d62bb01
8f58902
96c71eb
9eafb76
5ef31ef
ce608de
13097f5
3498c9f
bd780e5
4270a6a
67f6645
370d41c
72d54ea
152d6e3
059aa4e
51ec882
bc32192
822bd6d
4798915
19ac452
931b25c
9ea29bb
61eb533
b4ad269
8ec8232
a95bf13
961697d
749ac86
d759f43
c7cf8a2
b4025ed
232cc0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import * as protos from '../../protos/protos'; | ||
import { | ||
BasicClusterConfig, | ||
ICluster, | ||
SetClusterMetadataOptions, | ||
} from '../cluster'; | ||
import {google} from '../../protos/protos'; | ||
|
||
export class ClusterUtils { | ||
static noConfigError = | ||
'Must specify either serve_nodes or all of the autoscaling configurations (min_serve_nodes, max_serve_nodes, and cpu_utilization_percent).'; | ||
static allConfigError = | ||
'Cannot specify both serve_nodes and autoscaling configurations (min_serve_nodes, max_serve_nodes, and cpu_utilization_percent).'; | ||
static incompleteConfigError = | ||
'All of autoscaling configurations must be specified at the same time (min_serve_nodes, max_serve_nodes, and cpu_utilization_percent).'; | ||
|
||
static validateClusterMetadata( | ||
metadata: SetClusterMetadataOptions | BasicClusterConfig | ||
): void { | ||
if (metadata.nodes) { | ||
if ( | ||
metadata.minServeNodes || | ||
metadata.maxServeNodes || | ||
metadata.cpuUtilizationPercent | ||
) { | ||
throw new Error(this.allConfigError); | ||
} | ||
} else { | ||
if ( | ||
metadata.minServeNodes || | ||
metadata.maxServeNodes || | ||
metadata.cpuUtilizationPercent | ||
) { | ||
if ( | ||
!( | ||
metadata.minServeNodes && | ||
metadata.maxServeNodes && | ||
metadata.cpuUtilizationPercent | ||
) | ||
) { | ||
throw new Error(this.incompleteConfigError); | ||
} | ||
} else { | ||
throw new Error(this.noConfigError); | ||
} | ||
} | ||
} | ||
static getUpdateMask(metadata: SetClusterMetadataOptions): string[] { | ||
const updateMask: string[] = []; | ||
if (metadata.nodes) { | ||
updateMask.push('serve_nodes'); | ||
if ( | ||
!( | ||
metadata.minServeNodes || | ||
metadata.maxServeNodes || | ||
metadata.cpuUtilizationPercent | ||
) | ||
) { | ||
updateMask.push('cluster_config.cluster_autoscaling_config'); | ||
} | ||
} | ||
if (metadata.minServeNodes) { | ||
updateMask.push( | ||
'cluster_config.cluster_autoscaling_config.autoscaling_limits.min_serve_nodes' | ||
); | ||
} | ||
if (metadata.maxServeNodes) { | ||
updateMask.push( | ||
'cluster_config.cluster_autoscaling_config.autoscaling_limits.max_serve_nodes' | ||
); | ||
} | ||
if (metadata.cpuUtilizationPercent) { | ||
updateMask.push( | ||
'cluster_config.cluster_autoscaling_config.autoscaling_targets.cpu_utilization_percent' | ||
); | ||
} | ||
return updateMask; | ||
} | ||
|
||
static getClusterBaseConfig( | ||
metadata: SetClusterMetadataOptions | BasicClusterConfig, | ||
location: string | undefined | null, | ||
name: string | undefined | ||
): google.bigtable.admin.v2.ICluster { | ||
let clusterConfig; | ||
if ( | ||
metadata.cpuUtilizationPercent || | ||
metadata.minServeNodes || | ||
metadata.maxServeNodes | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what happens if only one of these are set? Let's say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we use this in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When creating instances we now validate inputs. |
||
) { | ||
clusterConfig = { | ||
clusterAutoscalingConfig: { | ||
autoscalingTargets: { | ||
cpuUtilizationPercent: metadata.cpuUtilizationPercent, | ||
}, | ||
autoscalingLimits: { | ||
minServeNodes: metadata.minServeNodes, | ||
maxServeNodes: metadata.maxServeNodes, | ||
}, | ||
}, | ||
}; | ||
} | ||
return Object.assign( | ||
{}, | ||
name ? {name} : null, | ||
location ? {location} : null, | ||
clusterConfig ? {clusterConfig} : null, | ||
metadata.nodes ? {serveNodes: metadata.nodes} : null | ||
); | ||
} | ||
|
||
static getClusterFromMetadata( | ||
metadata: SetClusterMetadataOptions, | ||
location: string | undefined | null, | ||
name: string | ||
): google.bigtable.admin.v2.ICluster { | ||
const cluster: ICluster | SetClusterMetadataOptions = Object.assign( | ||
{}, | ||
this.getClusterBaseConfig(metadata, location, name), | ||
metadata | ||
); | ||
delete (cluster as SetClusterMetadataOptions).nodes; | ||
delete (cluster as SetClusterMetadataOptions).minServeNodes; | ||
delete (cluster as SetClusterMetadataOptions).maxServeNodes; | ||
delete (cluster as SetClusterMetadataOptions).cpuUtilizationPercent; | ||
return cluster as ICluster; | ||
} | ||
|
||
static getRequestFromMetadata( | ||
metadata: SetClusterMetadataOptions, | ||
location: string | undefined | null, | ||
name: string | ||
): protos.google.bigtable.admin.v2.IPartialUpdateClusterRequest { | ||
return { | ||
cluster: this.getClusterFromMetadata(metadata, location, name), | ||
updateMask: {paths: this.getUpdateMask(metadata)}, | ||
}; | ||
} | ||
} |
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.
is this considered breaking if we change something from required to optional? I'm guessing it would be the other way around, but want to confirm :)
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.
Here we are changing something from required to optional so that would make it not a breaking change since users can still pass just
nodes
in. This is where we makenodes
optional because instead the user can provide autoscaling parameters. These parameters will be validated against a new validation function and an error will be thrown if an invalid combination of parameters are provided.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.
I would not consider switching from required to optional for TypeScript breaking 👍