def __ensure_provisioning_writes(
        table_name, key_name, num_consec_write_checks):
    """ Ensure that provisioning of writes is correct

    :type table_name: str
    :param table_name: Name of the DynamoDB table
    :type key_name: str
    :param key_name: Configuration option key name
    :type num_consec_write_checks: int
    :param num_consec_write_checks: How many consecutive checks have we had
    :returns: (bool, int, int)
        update_needed, updated_write_units, num_consec_write_checks
    """
    if not get_table_option(key_name, 'enable_writes_autoscaling'):
        logger.info(
            '{0} - Autoscaling of writes has been disabled'.format(table_name))
        return False, dynamodb.get_provisioned_table_write_units(table_name), 0

    update_needed = False
    try:
        lookback_window_start = get_table_option(
            key_name, 'lookback_window_start')
        lookback_period = get_table_option(key_name, 'lookback_period')
        current_write_units = dynamodb.get_provisioned_table_write_units(
            table_name)
        consumed_write_units_percent = \
            table_stats.get_consumed_write_units_percent(
                table_name, lookback_window_start, lookback_period)
        throttled_write_count = \
            table_stats.get_throttled_write_event_count(
                table_name, lookback_window_start, lookback_period)
        throttled_by_provisioned_write_percent = \
            table_stats.get_throttled_by_provisioned_write_event_percent(
                table_name, lookback_window_start, lookback_period)
        throttled_by_consumed_write_percent = \
            table_stats.get_throttled_by_consumed_write_percent(
                table_name, lookback_window_start, lookback_period)
        writes_upper_threshold = \
            get_table_option(key_name, 'writes_upper_threshold')
        writes_lower_threshold = \
            get_table_option(key_name, 'writes_lower_threshold')
        throttled_writes_upper_threshold = \
            get_table_option(key_name, 'throttled_writes_upper_threshold')
        increase_writes_unit = \
            get_table_option(key_name, 'increase_writes_unit')
        increase_writes_with = \
            get_table_option(key_name, 'increase_writes_with')
        decrease_writes_unit = \
            get_table_option(key_name, 'decrease_writes_unit')
        decrease_writes_with = \
            get_table_option(key_name, 'decrease_writes_with')
        min_provisioned_writes = \
            get_table_option(key_name, 'min_provisioned_writes')
        max_provisioned_writes = \
            get_table_option(key_name, 'max_provisioned_writes')
        num_write_checks_before_scale_down = \
            get_table_option(key_name, 'num_write_checks_before_scale_down')
        num_write_checks_reset_percent = \
            get_table_option(key_name, 'num_write_checks_reset_percent')
        increase_throttled_by_provisioned_writes_unit = \
            get_table_option(
                key_name, 'increase_throttled_by_provisioned_writes_unit')
        increase_throttled_by_provisioned_writes_scale = \
            get_table_option(
                key_name, 'increase_throttled_by_provisioned_writes_scale')
        increase_throttled_by_consumed_writes_unit = \
            get_table_option(
                key_name, 'increase_throttled_by_consumed_writes_unit')
        increase_throttled_by_consumed_writes_scale = \
            get_table_option(
                key_name, 'increase_throttled_by_consumed_writes_scale')
        increase_consumed_writes_unit = \
            get_table_option(key_name, 'increase_consumed_writes_unit')
        increase_consumed_writes_with = \
            get_table_option(key_name, 'increase_consumed_writes_with')
        increase_consumed_writes_scale = \
            get_table_option(key_name, 'increase_consumed_writes_scale')
        decrease_consumed_writes_unit = \
            get_table_option(key_name, 'decrease_consumed_writes_unit')
        decrease_consumed_writes_with = \
            get_table_option(key_name, 'decrease_consumed_writes_with')
        decrease_consumed_writes_scale = \
            get_table_option(key_name, 'decrease_consumed_writes_scale')
    except JSONResponseError:
        raise
    except BotoServerError:
        raise

    # Set the updated units to the current read unit value
    updated_write_units = current_write_units

    # Reset consecutive write count if num_write_checks_reset_percent
    # is reached
    if num_write_checks_reset_percent:

        if consumed_write_units_percent >= num_write_checks_reset_percent:

            logger.info(
                '{0} - Resetting the number of consecutive '
                'write checks. Reason: Consumed percent {1} is '
                'greater than reset percent: {2}'.format(
                    table_name,
                    consumed_write_units_percent,
                    num_write_checks_reset_percent))

            num_consec_write_checks = 0

    # Exit if up scaling has been disabled
    if not get_table_option(key_name, 'enable_writes_up_scaling'):
        logger.debug(
            '{0} - Up scaling event detected. No action taken as scaling '
            'up writes has been disabled in the configuration'.format(
                table_name))

    else:

        # If local/granular values not specified use global values
        increase_consumed_writes_unit = \
            increase_consumed_writes_unit or increase_writes_unit
        increase_throttled_by_provisioned_writes_unit = (
            increase_throttled_by_provisioned_writes_unit
            or increase_writes_unit)
        increase_throttled_by_consumed_writes_unit = \
            increase_throttled_by_consumed_writes_unit or increase_writes_unit

        increase_consumed_writes_with = \
            increase_consumed_writes_with or increase_writes_with

        # Initialise variables to store calculated provisioning
        throttled_by_provisioned_calculated_provisioning = scale_reader(
            increase_throttled_by_provisioned_writes_scale,
            throttled_by_provisioned_write_percent)
        throttled_by_consumed_calculated_provisioning = scale_reader(
            increase_throttled_by_consumed_writes_scale,
            throttled_by_consumed_write_percent)
        consumed_calculated_provisioning = scale_reader(
            increase_consumed_writes_scale, consumed_write_units_percent)
        throttled_count_calculated_provisioning = 0
        calculated_provisioning = 0

        # Increase needed due to high throttled to provisioned ratio
        if throttled_by_provisioned_calculated_provisioning:

            if increase_throttled_by_provisioned_writes_unit == 'percent':
                throttled_by_provisioned_calculated_provisioning = \
                    calculators.increase_writes_in_percent(
                        current_write_units,
                        throttled_by_provisioned_calculated_provisioning,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)
            else:
                throttled_by_provisioned_calculated_provisioning = \
                    calculators.increase_writes_in_units(
                        current_write_units,
                        throttled_by_provisioned_calculated_provisioning,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)

        # Increase needed due to high throttled to consumed ratio
        if throttled_by_consumed_calculated_provisioning:

            if increase_throttled_by_consumed_writes_unit == 'percent':
                throttled_by_consumed_calculated_provisioning = \
                    calculators.increase_writes_in_percent(
                        current_write_units,
                        throttled_by_consumed_calculated_provisioning,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)
            else:
                throttled_by_consumed_calculated_provisioning = \
                    calculators.increase_writes_in_units(
                        current_write_units,
                        throttled_by_consumed_calculated_provisioning,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)

        # Increase needed due to high CU consumption
        if consumed_calculated_provisioning:

            if increase_consumed_writes_unit == 'percent':
                consumed_calculated_provisioning = \
                    calculators.increase_writes_in_percent(
                        current_write_units,
                        consumed_calculated_provisioning,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)
            else:
                consumed_calculated_provisioning = \
                    calculators.increase_writes_in_units(
                        current_write_units,
                        consumed_calculated_provisioning,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)

        elif (writes_upper_threshold
              and consumed_write_units_percent > writes_upper_threshold
              and not increase_consumed_writes_scale):

            if increase_consumed_writes_unit == 'percent':
                consumed_calculated_provisioning = \
                    calculators.increase_writes_in_percent(
                        current_write_units,
                        increase_consumed_writes_with,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)
            else:
                consumed_calculated_provisioning = \
                    calculators.increase_writes_in_units(
                        current_write_units,
                        increase_consumed_writes_with,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)

        # Increase needed due to high throttling
        if (throttled_writes_upper_threshold and throttled_write_count >
                throttled_writes_upper_threshold):

            if increase_writes_unit == 'percent':
                throttled_count_calculated_provisioning = \
                    calculators.increase_writes_in_percent(
                        updated_write_units,
                        increase_writes_with,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)
            else:
                throttled_count_calculated_provisioning = \
                    calculators.increase_writes_in_units(
                        updated_write_units,
                        increase_writes_with,
                        get_table_option(key_name, 'max_provisioned_writes'),
                        consumed_write_units_percent,
                        table_name)

        # Determine which metric requires the most scaling
        if (throttled_by_provisioned_calculated_provisioning >
                calculated_provisioning):
            calculated_provisioning = \
                throttled_by_provisioned_calculated_provisioning
            scale_reason = (
                "due to throttled events by provisioned "
                "units threshold being exceeded")
        if (throttled_by_consumed_calculated_provisioning
                > calculated_provisioning):
            calculated_provisioning = \
                throttled_by_consumed_calculated_provisioning
            scale_reason = (
                "due to throttled events by consumed "
                "units threshold being exceeded")
        if consumed_calculated_provisioning > calculated_provisioning:
            calculated_provisioning = consumed_calculated_provisioning
            scale_reason = "due to consumed threshold being exceeded"
        if throttled_count_calculated_provisioning > calculated_provisioning:
            calculated_provisioning = throttled_count_calculated_provisioning
            scale_reason = "due to throttled events threshold being exceeded"

        if calculated_provisioning > current_write_units:
            logger.info(
                '{0} - Resetting the number of consecutive '
                'write checks. Reason: scale up {1}'.format(
                    table_name, scale_reason))
            num_consec_write_checks = 0
            update_needed = True
            updated_write_units = calculated_provisioning

    # Decrease needed due to low CU consumption
    if not update_needed:
        # If local/granular values not specified use global values
        decrease_consumed_writes_unit = \
            decrease_consumed_writes_unit or decrease_writes_unit

        decrease_consumed_writes_with = \
            decrease_consumed_writes_with or decrease_writes_with

        # Initialise variables to store calculated provisioning
        consumed_calculated_provisioning = scale_reader_decrease(
            decrease_consumed_writes_scale,
            consumed_write_units_percent)
        calculated_provisioning = None

        # Exit if down scaling has been disabled
        if not get_table_option(key_name, 'enable_writes_down_scaling'):
            logger.debug(
                '{0} - Down scaling event detected. No action taken as scaling'
                ' down writes has been disabled in the configuration'.format(
                    table_name))
        # Exit if writes == 0% and downscaling has been disabled at 0%
        elif (consumed_write_units_percent == 0 and not
                get_table_option(
                    key_name, 'allow_scaling_down_writes_on_0_percent')):
            logger.info(
                '{0} - Down scaling event detected. No action taken as scaling'
                ' down writes is not done when usage is at 0%'.format(
                    table_name))
        # Exit if writes are still throttled
        elif (throttled_writes_upper_threshold
              and throttled_write_count > throttled_writes_upper_threshold):
            logger.info(
                '{0} - Down scaling event detected. No action taken as there'
                ' are still throttled writes'.format(table_name))
        else:
            if consumed_calculated_provisioning:
                if decrease_consumed_writes_unit == 'percent':
                    calculated_provisioning = \
                        calculators.decrease_writes_in_percent(
                            updated_write_units,
                            consumed_calculated_provisioning,
                            get_table_option(
                                key_name, 'min_provisioned_writes'),
                            table_name)
                else:
                    calculated_provisioning = \
                        calculators.decrease_writes_in_units(
                            updated_write_units,
                            consumed_calculated_provisioning,
                            get_table_option(
                                key_name, 'min_provisioned_writes'),
                            table_name)
            elif (writes_lower_threshold
                  and consumed_write_units_percent < writes_lower_threshold
                  and not decrease_consumed_writes_scale):
                if decrease_consumed_writes_unit == 'percent':
                    calculated_provisioning = \
                        calculators.decrease_writes_in_percent(
                            updated_write_units,
                            decrease_consumed_writes_with,
                            get_table_option(
                                key_name, 'min_provisioned_writes'),
                            table_name)
                else:
                    calculated_provisioning = \
                        calculators.decrease_writes_in_units(
                            updated_write_units,
                            decrease_consumed_writes_with,
                            get_table_option(
                                key_name, 'min_provisioned_writes'),
                            table_name)

            if (calculated_provisioning and
                    current_write_units != calculated_provisioning):
                num_consec_write_checks += 1

                if num_consec_write_checks >= \
                        num_write_checks_before_scale_down:
                    update_needed = True
                    updated_write_units = calculated_provisioning

    # Never go over the configured max 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(
                'Will not increase writes over max-provisioned-writes '
                'limit ({0} writes)'.format(updated_write_units))

    # Ensure that we have met the min-provisioning
    if min_provisioned_writes:
        if int(min_provisioned_writes) > int(updated_write_units):
            update_needed = True
            updated_write_units = int(min_provisioned_writes)
            logger.info(
                '{0} - Increasing writes to meet min-provisioned-writes '
                'limit ({1} writes)'.format(table_name, updated_write_units))

    if calculators.is_consumed_over_proposed(
            current_write_units,
            updated_write_units,
            consumed_write_units_percent):
        update_needed = False
        updated_write_units = current_write_units
        logger.info(
            '{0} - Consumed is over proposed write units. Will leave table at '
            'current setting.'.format(table_name))

    logger.info('{0} - Consecutive write checks {1}/{2}'.format(
        table_name,
        num_consec_write_checks,
        num_write_checks_before_scale_down))

    return update_needed, updated_write_units, num_consec_write_checks
Ensure that provisioning of writes is correct

    :type table_name: str
    :param table_name: Name of the DynamoDB table
    :type key_name: str
    :param key_name: Configuration option key name
    :type num_consec_write_checks: int
    :param num_consec_write_checks: How many consecutive checks have we had
    :returns: (bool, int, int)
        update_needed, updated_write_units, num_consec_write_checks