From 76fb09ac35f2d551c8f21115c6aa082f4a9cd9c7 Mon Sep 17 00:00:00 2001 From: Bill Prin Date: Mon, 26 Jun 2017 14:48:04 -0700 Subject: [PATCH 1/2] Update Bigtable Programmatic Scaling Example * Rename "autoscaling" to "metricscaler" and the the term "programmatic scaling" * Remove `strategies.py` to simplify example * Fix wrong sleep length bug * Add maximum node count --- bigtable/autoscaler/strategies.py | 51 ------------------- bigtable/autoscaler/strategies_test.py | 30 ----------- .../{autoscaler => metricscaler}/README.rst | 12 ++--- .../README.rst.in | 4 +- .../metricscaler.py} | 48 +++++++++++------ .../metricscaler_test.py} | 16 +++--- .../requirements.txt | 0 7 files changed, 49 insertions(+), 112 deletions(-) delete mode 100644 bigtable/autoscaler/strategies.py delete mode 100644 bigtable/autoscaler/strategies_test.py rename bigtable/{autoscaler => metricscaler}/README.rst (92%) rename bigtable/{autoscaler => metricscaler}/README.rst.in (92%) rename bigtable/{autoscaler/autoscaler.py => metricscaler/metricscaler.py} (77%) rename bigtable/{autoscaler/autoscaler_test.py => metricscaler/metricscaler_test.py} (87%) rename bigtable/{autoscaler => metricscaler}/requirements.txt (100%) diff --git a/bigtable/autoscaler/strategies.py b/bigtable/autoscaler/strategies.py deleted file mode 100644 index 9b1c7483b9b..00000000000 --- a/bigtable/autoscaler/strategies.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2017 Google Inc. -# -# 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 -# -# http://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. - -"""This module contains various strategies for upscaling and downscaling -Bigtable.""" - - -def _upscale_incremental_strategy(current_nodes): - """Simple scaling strategy, increase nodes by 2.""" - return current_nodes + 2 - - -UPSCALE_STRATEGIES = { - 'incremental': _upscale_incremental_strategy -} - -"""UPSCALE_STRATEGIES contains a dict of functions for scaling Bigtable. - -The function signature should accept the current number of nodes and return -the new number of nodes to scale to. -""" - - -def _downscale_incremental_strategy(current_nodes): - """Simple downscale strategy: decrease nodes by 2.""" - new_nodes = current_nodes - 2 - if new_nodes < 3: # 3 is minimum number of CBT nodes - return 3 - return new_nodes - - -DOWNSCALE_STRATEGIES = { - 'incremental': _downscale_incremental_strategy -} - -"""DOWNSCALE_STRATEGIES contains a dict of functions for scaling Bigtable. - -The function signature should accept the current number of nodes and return -the new number of nodes to scale to. -""" diff --git a/bigtable/autoscaler/strategies_test.py b/bigtable/autoscaler/strategies_test.py deleted file mode 100644 index 57bbfd5f9b5..00000000000 --- a/bigtable/autoscaler/strategies_test.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2017 Google Inc. -# -# 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 -# -# http://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. - -"""Tests for autoscaler.py""" - -from strategies import DOWNSCALE_STRATEGIES -from strategies import UPSCALE_STRATEGIES - - -def test_downscale(): - downscale_strategy = DOWNSCALE_STRATEGIES['incremental'] - assert downscale_strategy(5) == 3 - assert downscale_strategy(4) == 3 - assert downscale_strategy(3) == 3 - - -def test_upscale(): - upscale_strategy = UPSCALE_STRATEGIES['incremental'] - assert upscale_strategy(3) == 5 diff --git a/bigtable/autoscaler/README.rst b/bigtable/metricscaler/README.rst similarity index 92% rename from bigtable/autoscaler/README.rst rename to bigtable/metricscaler/README.rst index b9c4be6a65f..6d4a373a30f 100644 --- a/bigtable/autoscaler/README.rst +++ b/bigtable/metricscaler/README.rst @@ -74,7 +74,7 @@ Install Dependencies Samples ------------------------------------------------------------------------------- -Autoscaling example +Metricscaling example +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -83,12 +83,12 @@ To run this sample: .. code-block:: bash - $ python autoscaler.py + $ python metricscaler.py - usage: autoscaler.py [-h] [--high_cpu_threshold HIGH_CPU_THRESHOLD] - [--low_cpu_threshold LOW_CPU_THRESHOLD] - [--short_sleep SHORT_SLEEP] [--long_sleep LONG_SLEEP] - bigtable_instance bigtable_cluster + usage: metricscaler.py [-h] [--high_cpu_threshold HIGH_CPU_THRESHOLD] + [--low_cpu_threshold LOW_CPU_THRESHOLD] + [--short_sleep SHORT_SLEEP] [--long_sleep LONG_SLEEP] + bigtable_instance bigtable_cluster Scales Cloud Bigtable clusters based on CPU usage. diff --git a/bigtable/autoscaler/README.rst.in b/bigtable/metricscaler/README.rst.in similarity index 92% rename from bigtable/autoscaler/README.rst.in rename to bigtable/metricscaler/README.rst.in index 21016b73d3f..09b1a295a48 100644 --- a/bigtable/autoscaler/README.rst.in +++ b/bigtable/metricscaler/README.rst.in @@ -20,8 +20,8 @@ setup: - install_deps samples: -- name: Autoscaling example - file: autoscaler.py +- name: Metricscaling example + file: metricscaler.py show_help: true cloud_client_library: true diff --git a/bigtable/autoscaler/autoscaler.py b/bigtable/metricscaler/metricscaler.py similarity index 77% rename from bigtable/autoscaler/autoscaler.py rename to bigtable/metricscaler/metricscaler.py index 32b3656e0c4..c1c5412ad13 100644 --- a/bigtable/autoscaler/autoscaler.py +++ b/bigtable/metricscaler/metricscaler.py @@ -21,7 +21,25 @@ from google.cloud import bigtable from google.cloud import monitoring -import strategies + +_MIN_NODE_COUNT = 3 +""" +The minimum number of nodes to use. The default minimum is 3. If you have a +lot of data, the rule of thumb is to not go below 2.5 TB per node for SSD +clusters, and 8 TB for HDD. The bigtable.googleapis.com/disk/bytes_used +metric is useful in figuring out the minimum number. +of nodes. +""" + +_MAX_NODE_COUNT = 30 +""" +The maximum number of nodes to use. The default maximum is 30 nodes per zone. +If you need more quota, you can request more by following the instructions +here. +""" + +_SIZE_CHANGE_STEP = 3 +"""The number of nodes to change the cluster by.""" def get_cpu_load(): @@ -62,21 +80,21 @@ def scale_bigtable(bigtable_instance, bigtable_cluster, scale_up): current_node_count = cluster.serve_nodes - if current_node_count <= 3 and not scale_up: - # Can't downscale lower than 3 nodes - return - if scale_up: - strategies_dict = strategies.UPSCALE_STRATEGIES + if current_node_count < _MAX_NODE_COUNT: + new_node_count = min(current_node_count + 3, _MAX_NODE_COUNT) + cluster.serve_nodes = new_node_count + cluster.update() + print('Scaled up from {} up to {} nodes.'.format( + current_node_count, new_node_count)) else: - strategies_dict = strategies.DOWNSCALE_STRATEGIES - - strategy = strategies_dict['incremental'] - new_node_count = strategy(cluster.serve_nodes) - cluster.serve_nodes = new_node_count - cluster.update() - print('Scaled from {} up to {} nodes.'.format( - current_node_count, new_node_count)) + if current_node_count > _MIN_NODE_COUNT: + new_node_count = max( + current_node_count - _SIZE_CHANGE_STEP, _MIN_NODE_COUNT) + cluster.serve_nodes = new_node_count + cluster.update() + print('Scaled down from {} up to {} nodes.'.format( + current_node_count, new_node_count)) # [END bigtable_scale] @@ -104,7 +122,7 @@ def main( time.sleep(long_sleep) elif cluster_cpu < low_cpu_threshold: scale_bigtable(bigtable_instance, bigtable_cluster, False) - time.sleep(short_sleep) + time.sleep(long_sleep) else: print('CPU within threshold, sleeping.') time.sleep(short_sleep) diff --git a/bigtable/autoscaler/autoscaler_test.py b/bigtable/metricscaler/metricscaler_test.py similarity index 87% rename from bigtable/autoscaler/autoscaler_test.py rename to bigtable/metricscaler/metricscaler_test.py index 5c710746648..7a151a0efd2 100644 --- a/bigtable/autoscaler/autoscaler_test.py +++ b/bigtable/metricscaler/metricscaler_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Unit and system tests for autoscaler.py""" +"""Unit and system tests for metricscaler.py""" import os import time @@ -20,9 +20,10 @@ from google.cloud import bigtable from mock import patch -from autoscaler import get_cpu_load -from autoscaler import main -from autoscaler import scale_bigtable +from metricscaler import _SIZE_CHANGE_STEP +from metricscaler import get_cpu_load +from metricscaler import main +from metricscaler import scale_bigtable # tests assume instance and cluster have the same ID BIGTABLE_INSTANCE = os.environ['BIGTABLE_CLUSTER'] @@ -49,7 +50,7 @@ def test_scale_bigtable(): cluster.reload() new_node_count = cluster.serve_nodes - assert (new_node_count == (original_node_count + 2)) + assert (new_node_count == (original_node_count + _SIZE_CHANGE_STEP)) scale_bigtable(BIGTABLE_INSTANCE, BIGTABLE_INSTANCE, False) time.sleep(3) @@ -59,10 +60,9 @@ def test_scale_bigtable(): # Unit test for logic - @patch('time.sleep') -@patch('autoscaler.get_cpu_load') -@patch('autoscaler.scale_bigtable') +@patch('metricscaler.get_cpu_load') +@patch('metricscaler.scale_bigtable') def test_main(scale_bigtable, get_cpu_load, sleep): SHORT_SLEEP = 5 LONG_SLEEP = 10 diff --git a/bigtable/autoscaler/requirements.txt b/bigtable/metricscaler/requirements.txt similarity index 100% rename from bigtable/autoscaler/requirements.txt rename to bigtable/metricscaler/requirements.txt From 7b3c555d3d831ea5f83e5ddf298362d135e25a39 Mon Sep 17 00:00:00 2001 From: Bill Prin Date: Mon, 26 Jun 2017 17:51:45 -0700 Subject: [PATCH 2/2] hegemonic review --- bigtable/metricscaler/metricscaler.py | 40 +++++++++++++-------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/bigtable/metricscaler/metricscaler.py b/bigtable/metricscaler/metricscaler.py index c1c5412ad13..8a61ca3eb23 100644 --- a/bigtable/metricscaler/metricscaler.py +++ b/bigtable/metricscaler/metricscaler.py @@ -22,25 +22,6 @@ from google.cloud import monitoring -_MIN_NODE_COUNT = 3 -""" -The minimum number of nodes to use. The default minimum is 3. If you have a -lot of data, the rule of thumb is to not go below 2.5 TB per node for SSD -clusters, and 8 TB for HDD. The bigtable.googleapis.com/disk/bytes_used -metric is useful in figuring out the minimum number. -of nodes. -""" - -_MAX_NODE_COUNT = 30 -""" -The maximum number of nodes to use. The default maximum is 30 nodes per zone. -If you need more quota, you can request more by following the instructions -here. -""" - -_SIZE_CHANGE_STEP = 3 -"""The number of nodes to change the cluster by.""" - def get_cpu_load(): """Returns the most recent Cloud Bigtable CPU load measurement. @@ -70,6 +51,23 @@ def scale_bigtable(bigtable_instance, bigtable_cluster, scale_up): bigtable_cluster (str): Cloud Bigtable cluster ID to scale scale_up (bool): If true, scale up, otherwise scale down """ + _MIN_NODE_COUNT = 3 + """ + The minimum number of nodes to use. The default minimum is 3. If you have a + lot of data, the rule of thumb is to not go below 2.5 TB per node for SSD + clusters, and 8 TB for HDD. The bigtable.googleapis.com/disk/bytes_used + metric is useful in figuring out the minimum number of nodes. + """ + + _MAX_NODE_COUNT = 30 + """ + The maximum number of nodes to use. The default maximum is 30 nodes per zone. + If you need more quota, you can request more by following the instructions + here. + """ + + _SIZE_CHANGE_STEP = 3 + """The number of nodes to change the cluster by.""" # [START bigtable_scale] bigtable_client = bigtable.Client(admin=True) instance = bigtable_client.instance(bigtable_instance) @@ -85,7 +83,7 @@ def scale_bigtable(bigtable_instance, bigtable_cluster, scale_up): new_node_count = min(current_node_count + 3, _MAX_NODE_COUNT) cluster.serve_nodes = new_node_count cluster.update() - print('Scaled up from {} up to {} nodes.'.format( + print('Scaled up from {} to {} nodes.'.format( current_node_count, new_node_count)) else: if current_node_count > _MIN_NODE_COUNT: @@ -93,7 +91,7 @@ def scale_bigtable(bigtable_instance, bigtable_cluster, scale_up): current_node_count - _SIZE_CHANGE_STEP, _MIN_NODE_COUNT) cluster.serve_nodes = new_node_count cluster.update() - print('Scaled down from {} up to {} nodes.'.format( + print('Scaled down from {} to {} nodes.'.format( current_node_count, new_node_count)) # [END bigtable_scale]