def get_consumed_write_units_percent(table_name, gsi_name, time_frame=300): """ Returns the number of consumed write units in percent :type table_name: str :param table_name: Name of the DynamoDB table :type gsi_name: str :param gsi_name: Name of the GSI :type time_frame: int :param time_frame: How many seconds to look at :returns: int -- Number of consumed writes """ metrics = __get_aws_metric( table_name, gsi_name, time_frame, 'ConsumedWriteCapacityUnits') if metrics: consumed_write_units = int( math.ceil(float(metrics[0]['Sum'])/float(time_frame))) else: consumed_write_units = 0 try: consumed_write_units_percent = int( math.ceil( float(consumed_write_units) / float(dynamodb.get_provisioned_gsi_write_units( table_name, gsi_name)) * 100)) except JSONResponseError: raise logger.info('{0} - GSI: {1} - Consumed write units: {2:d}%'.format( table_name, gsi_name, consumed_write_units_percent)) return consumed_write_units_percent
def get_consumed_write_units_percent(table_name, gsi_name, time_frame=300): """ Returns the number of consumed write units in percent :type table_name: str :param table_name: Name of the DynamoDB table :type gsi_name: str :param gsi_name: Name of the GSI :type time_frame: int :param time_frame: How many seconds to look at :returns: int -- Number of consumed writes """ metrics = cloudwatch_connection.get_metric_statistics( period=time_frame, start_time=datetime.utcnow()-timedelta(minutes=10, seconds=time_frame), end_time=datetime.utcnow()-timedelta(minutes=10), metric_name='ConsumedWriteCapacityUnits', namespace='AWS/DynamoDB', statistics=['Sum'], dimensions={ 'TableName': table_name, 'GlobalSecondaryIndexName': gsi_name }, unit='Count') if metrics: consumed_write_units = int( math.ceil(float(metrics[0]['Sum'])/float(time_frame))) else: consumed_write_units = 0 consumed_write_units_percent = int( math.ceil( float(consumed_write_units) / float(dynamodb.get_provisioned_gsi_write_units( table_name, gsi_name)) * 100)) logger.info('{0} - GSI: {1} - Consumed write units: {2:d}%'.format( table_name, gsi_name, consumed_write_units_percent)) return consumed_write_units_percent
def __update_throughput( table_name, table_key, gsi_name, gsi_key, read_units, write_units): """ Update throughput on the GSI :type table_name: str :param table_name: Name of the DynamoDB table :type table_key: str :param table_key: Table configuration option key name :type gsi_name: str :param gsi_name: Name of the GSI :type gsi_key: str :param gsi_key: Configuration option key name :type read_units: int :param read_units: New read unit provisioning :type write_units: int :param write_units: New write unit provisioning """ try: current_ru = dynamodb.get_provisioned_gsi_read_units( table_name, gsi_name) current_wu = dynamodb.get_provisioned_gsi_write_units( table_name, gsi_name) except JSONResponseError: raise # Check that we are in the right time frame if get_gsi_option(table_key, gsi_key, 'maintenance_windows'): if (not __is_maintenance_window(table_name, gsi_name, get_gsi_option( table_key, gsi_key, 'maintenance_windows'))): logger.warning( '{0} - GSI: {1} - ' 'Current time is outside maintenance window'.format( table_name, gsi_name)) return else: logger.info( '{0} - GSI: {1} - ' 'Current time is within maintenance window'.format( table_name, gsi_name)) # Check table status try: gsi_status = dynamodb.get_gsi_status(table_name, gsi_name) except JSONResponseError: raise logger.debug('{0} - GSI: {1} - GSI status is {2}'.format( table_name, gsi_name, gsi_status)) if gsi_status != 'ACTIVE': logger.warning( '{0} - GSI: {1} - Not performing throughput changes when GSI ' 'status is {2}'.format(table_name, gsi_name, gsi_status)) return # If this setting is True, we will only scale down when # BOTH reads AND writes are low if get_gsi_option(table_key, gsi_key, 'always_decrease_rw_together'): read_units, write_units = __calculate_always_decrease_rw_values( table_name, gsi_name, read_units, current_ru, write_units, current_wu) if read_units == current_ru and write_units == current_wu: logger.info('{0} - GSI: {1} - No changes to perform'.format( table_name, gsi_name)) return if not get_global_option('dry_run'): dynamodb.update_gsi_provisioning( table_name, table_key, gsi_name, gsi_key, int(read_units), int(write_units)) logger.info( '{0} - GSI: {1} - ' 'Provisioning updated to {2} reads and {3} writes'.format( table_name, gsi_name, read_units, write_units))
def __ensure_provisioning_writes(table_name, table_key, gsi_name, gsi_key): """ Ensure that provisioning of writes is correct :type table_name: str :param table_name: Name of the DynamoDB table :type table_key: str :param table_key: Table configuration option key name :type gsi_name: str :param gsi_name: Name of the GSI :type gsi_key: str :param gsi_key: Configuration option key name :returns: (bool, int) -- update_needed, updated_write_units """ if not get_gsi_option(table_key, gsi_key, 'enable_writes_autoscaling'): logger.info( '{0} - GSI: {1} - ' 'Autoscaling of writes has been disabled'.format( table_name, gsi_name)) return False, dynamodb.get_provisioned_gsi_write_units( table_name, gsi_name) update_needed = False try: updated_write_units = dynamodb.get_provisioned_gsi_write_units( table_name, gsi_name) consumed_write_units_percent = \ gsi_stats.get_consumed_write_units_percent(table_name, gsi_name) throttled_write_count = \ gsi_stats.get_throttled_write_event_count(table_name, gsi_name) writes_upper_threshold = \ get_gsi_option(table_key, gsi_key, 'writes_upper_threshold') writes_lower_threshold = \ get_gsi_option(table_key, gsi_key, 'writes_lower_threshold') throttled_writes_upper_threshold = \ get_gsi_option( table_key, gsi_key, 'throttled_writes_upper_threshold') increase_writes_unit = \ get_gsi_option(table_key, gsi_key, 'increase_writes_unit') increase_writes_with = \ get_gsi_option(table_key, gsi_key, 'increase_writes_with') decrease_writes_unit = \ get_gsi_option(table_key, gsi_key, 'decrease_writes_unit') decrease_writes_with = \ get_gsi_option(table_key, gsi_key, 'decrease_writes_with') max_provisioned_writes = \ get_gsi_option(table_key, gsi_key, 'max_provisioned_writes') except JSONResponseError: raise except BotoServerError: raise # Check if we should update write provisioning if (consumed_write_units_percent == 0 and not get_gsi_option( table_key, gsi_key, 'allow_scaling_down_writes_on_0_percent')): logger.info( '{0} - GSI: {1} - ' 'Scaling down writes is not done when usage is at 0%'.format( table_name, gsi_name)) elif consumed_write_units_percent >= writes_upper_threshold: if increase_writes_unit == 'percent': updated_provisioning = calculators.increase_writes_in_percent( updated_write_units, increase_writes_with, table_name, table_key, gsi_name, gsi_key) else: updated_provisioning = calculators.increase_writes_in_units( updated_write_units, increase_writes_with, table_name, table_key, gsi_name, gsi_key) if updated_write_units != updated_provisioning: update_needed = True updated_write_units = updated_provisioning elif throttled_write_count > throttled_writes_upper_threshold: if throttled_writes_upper_threshold > 0: if increase_writes_unit == 'percent': updated_provisioning = calculators.increase_writes_in_percent( updated_write_units, increase_writes_with, table_name, table_key, gsi_name, gsi_key) else: updated_provisioning = calculators.increase_writes_in_units( updated_write_units, increase_writes_with, table_name, table_key, gsi_name, gsi_key) if updated_write_units != updated_provisioning: update_needed = True updated_write_units = updated_provisioning elif consumed_write_units_percent <= writes_lower_threshold: if decrease_writes_unit == 'percent': updated_provisioning = calculators.decrease_writes_in_percent( updated_write_units, decrease_writes_with, table_name, table_key, gsi_name, gsi_key) else: updated_provisioning = calculators.decrease_writes_in_units( updated_write_units, decrease_writes_with, table_name, table_key, gsi_name, gsi_key) if updated_write_units != updated_provisioning: update_needed = True updated_write_units = updated_provisioning if max_provisioned_writes: if int(updated_write_units) > int(max_provisioned_writes): update_needed = True updated_write_units = int(max_provisioned_writes) logger.info( '{0} - GSI: {1} - ' 'Will not increase writes over gsi-max-provisioned-writes ' 'limit ({2} writes)'.format( table_name, gsi_name, updated_write_units)) return update_needed, int(updated_write_units)