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

feat: Autoscaler #1077

Merged
merged 35 commits into from
May 31, 2022
Merged

feat: Autoscaler #1077

merged 35 commits into from
May 31, 2022

Conversation

danieljbruce
Copy link
Contributor

This feature allows the user to set properties when creating a cluster or updating a cluster that turn on the autoscaler or turn it off to use manual scaling.

@danieljbruce danieljbruce requested review from a team as code owners April 21, 2022 21:32
@product-auto-label product-auto-label bot added size: l Pull request size is large. api: bigtable Issues related to the googleapis/nodejs-bigtable API. labels Apr 21, 2022
@danieljbruce danieljbruce marked this pull request as draft April 21, 2022 21:33
@danieljbruce danieljbruce marked this pull request as ready for review April 22, 2022 17:43
@@ -75,7 +76,10 @@ export type GetClustersCallback = (
apiResponse?: google.bigtable.admin.v2.IListClustersResponse
) => void;
export interface SetClusterMetadataOptions {
nodes: number;
nodes?: number;
Copy link
Contributor

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 :)

Copy link
Contributor Author

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 make nodes 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.

Copy link
Contributor

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 👍

src/cluster.ts Outdated Show resolved Hide resolved
src/instance.ts Show resolved Hide resolved
if (
metadata.cpuUtilizationPercent ||
metadata.minServeNodes ||
metadata.maxServeNodes
Copy link
Contributor

Choose a reason for hiding this comment

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

what happens if only one of these are set? Let's say metadata.minServeNodes = 4, but the rest are unset. Could that cause issues?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When we use this in instance.ts we validate beforehand so in this case an error would be thrown as required, but after investigating this further, I think I will add validation to createInstance as well to make sure an error gets thrown if properties are not set properly for each cluster.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When creating instances we now validate inputs.

assert.equal(e.message, ClusterUtils.noConfigError);
}
});
it('By providing too much cluster configurations', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit - reword to express that the user entered manual AND autoscaling configurations

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

system-test/cluster.ts Outdated Show resolved Hide resolved
Copy link
Contributor Author

@danieljbruce danieljbruce left a comment

Choose a reason for hiding this comment

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

Initial follow-up to first review.

if (
metadata.cpuUtilizationPercent ||
metadata.minServeNodes ||
metadata.maxServeNodes
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When we use this in instance.ts we validate beforehand so in this case an error would be thrown as required, but after investigating this further, I think I will add validation to createInstance as well to make sure an error gets thrown if properties are not set properly for each cluster.

system-test/cluster.ts Outdated Show resolved Hide resolved
assert.equal(e.message, ClusterUtils.noConfigError);
}
});
it('By providing too much cluster configurations', async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

it('Create an instance with clusters for manual scaling', async () => {
await checkMetadata(cluster, 2);
});
it('Create an instance and then create a cluster for manual scaling', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit - here and in all other it(.. descriptions, I think they should be reworded slightly; for example, here I would change it to say:

it('should create an instance and then create a cluster with manual scaling')

Let me know your thoughts :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Very good point. I'll look at some of the other PRs and make sure that they have these descriptions as well.

Copy link
Contributor Author

@danieljbruce danieljbruce left a comment

Choose a reason for hiding this comment

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

Addressed two more outstanding comments

it('Create an instance with clusters for manual scaling', async () => {
await checkMetadata(cluster, 2);
});
it('Create an instance and then create a cluster for manual scaling', async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Very good point. I'll look at some of the other PRs and make sure that they have these descriptions as well.

if (
metadata.cpuUtilizationPercent ||
metadata.minServeNodes ||
metadata.maxServeNodes
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When creating instances we now validate inputs.

static incompleteConfigError =
'All of autoscaling configurations must be specified at the same time (min_serve_nodes, max_serve_nodes, and cpu_utilization_percent).';

static validateMetadata(
Copy link
Contributor

Choose a reason for hiding this comment

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

I would rename this validateCreateClusterMetadata as we are checking for all autoscaling settings set.

Copy link
Contributor Author

@danieljbruce danieljbruce May 10, 2022

Choose a reason for hiding this comment

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

How about validateClusterMetadata since sometimes this check is done when updating a cluster as well as creating it?

assert.equal(e.message, ClusterUtils.allConfigError);
}
});
it('should throw an error providing all autoscaling configurations', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

should this say "should throw an error when missing some autoscaling configurations"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes!

});
it('should create an instance and then create clusters for automatic scaling', async () => {
const clusterId: string = generateId('cluster');
await createStandardNewInstance(clusterId, 2);
Copy link
Contributor

Choose a reason for hiding this comment

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

why are we creating a cluster with manual scaling for this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test is designed to capture a particular edge case. The test before this already covers the case where we create an instance that has clusters with automatic scaling. In this case we want to make sure everything works if we create an instance and then create a cluster since that user experience uses a different function entirely.

This test case does the following:

  1. createStandardNewInstance creates an instance with a cluster that has manual scaling
  2. cluster.create(createClusterOptions) creates a cluster with automatic scaling on this instance
  3. checkMetadata ensures that the metadata for the cluster created with automatic scaling has nodes, minServeNodes, maxServeNodes, cpuUtilizationPercent set correctly.


it('should change nodes for manual scaling', async () => {
const updateNodes = 5;
assert.notEqual(startingNodes, updateNodes);
Copy link
Contributor

Choose a reason for hiding this comment

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

this check is comparing two int variables - if you want to have this, I would instead get the creater cluster's nodes, then compare the created cluster's nodes to the updateNodes value.

Copy link
Contributor Author

@danieljbruce danieljbruce May 10, 2022

Choose a reason for hiding this comment

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

checkMetadata does the second thing you mentioned though it checks metadata.serveNodes equals updateNodes rather than checking metadata.serveNodes does not equal startingNodes. This assert.notEqual is more of a maintainability thing to ensure the programmer doesn't set updateNodes to equal startingNodes and then have the test pass when setMetadata doesn't actually work properly. If it's confusing though then we can remove it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is providing much in terms of autoscaling assertions, so I would remove it in favor of the checks you already mentioned.

});
});
describe('Update cluster', () => {
describe('Starting from manual scaling', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would rename this description to something like "updating manual scaling for a cluster"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

assert.equal(e.message, ClusterUtils.allConfigError);
}
});
it('should throw an error providing all autoscaling configurations', async () => {
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 this is the wrong description for this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah. This is a confusing error. Related to the comment above. Changing this now.

async function checkMetadata(
cluster: Cluster,
compareValues: SetClusterMetadataOptions,
isConfigDefined: boolean
Copy link
Contributor

Choose a reason for hiding this comment

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

why are we passing this into this function? Shouldn't we be able to determine this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is meant to be the expected value rather than the actual value of an assertion check. The purpose of passing isConfigDefined in is just to make sure the test is more thorough. For example, if we have a test that creates a cluster with automatic scaling and then we find the metadata fetched has no clusterConfig defined then we want it to fail on the line of code that says:

assert.equal(isConfigDefined, false);

@bcoe bcoe self-assigned this May 18, 2022
src/cluster.ts Outdated
callback(err, resp);
}
);
const setMetadataWithLocation = (location: string, name: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would remove this logic if we think it's currently not needed based on your testing.

metadata
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (cluster as any).nodes;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you should have to case to any, assuming that these are properties on ICluster.

@danieljbruce danieljbruce added the owlbot:run Add this label to trigger the Owlbot post processor. label May 31, 2022
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label May 31, 2022
@danieljbruce danieljbruce merged commit e5f6fdb into googleapis:main May 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: bigtable Issues related to the googleapis/nodejs-bigtable API. size: l Pull request size is large.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants