Skip to content

Commit

Permalink
Add archive bytes limit option to config
Browse files Browse the repository at this point in the history
This sets the max length of the archive in bytes. If the archive
grows larger than this limit the archive is trimmed until it
satisfies the limit.

For k8s products this limit is 1MiB or 1048576 bytes. If the limit
is set to 0 there is no size limit. The archive can grow as large
as the retention period.
  • Loading branch information
smarlowucf committed Jan 10, 2024
1 parent f946719 commit 0a5f1ad
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 8 deletions.
37 changes: 30 additions & 7 deletions csp_billing_adapter/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,29 @@

import functools
import logging
import json

from csp_billing_adapter.config import Config
from csp_billing_adapter.utils import retry_on_exception

log = logging.getLogger('CSPBillingAdapter')

DEFAULT_RETENTION_PERIOD = 6 # in months
DEFAULT_BYTES_LIMIT = 0


def append_metering_records(
archive: list,
billing_record: dict,
max_length: int
max_length: int,
max_bytes: int
) -> list:
"""
Append usage and metering records to the archive
If archive is larger than max length, drop the oldest record.
If archive is larger than max length drop the oldest
record. If the archive is larger than the max bytes
limit trim it until it satisfies the limit.
:param archive:
The list of meterings and usage records to append to.
Expand All @@ -44,17 +49,34 @@ def append_metering_records(
metering and usage records to be archived.
:param max_length:
The max size of the archive list.
:param max_bytes:
The max size in bytes of the archive.
:return:
The archive of meterings and usage records with the
billing_record appended. If archive ends up greater
than max lengh the first (oldest) record is dropped.
than max lengh or max bytes the archive is trimmed
as necessary to satisfy both max_length and
max_bytes.
"""
archive.append(billing_record)

if len(archive) > max_length:
return archive[1:]
else:
return archive
archive = archive[1:]

if max_bytes > 1:
# Treat 0 and 1 the same. Disable max bytes option.
# This prevents infitite loop when value is 1 since
# empty list is 2 bytes.
while True:
# Trim archive until it is smaller than max bytes
archive_size = len(bytes(json.dumps(archive), 'utf-8'))

if archive_size > max_bytes:
archive = archive[1:]
else:
break

return archive


def archive_record(
Expand Down Expand Up @@ -88,7 +110,8 @@ def archive_record(
archive = append_metering_records(
archive,
billing_record,
config.archive_retention_period or DEFAULT_RETENTION_PERIOD
config.archive_retention_period or DEFAULT_RETENTION_PERIOD,
config.archive_bytes_limit or DEFAULT_BYTES_LIMIT
)

retry_on_exception(
Expand Down
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ reporting_interval:
archive_retention_period:
Sets the time in months that meterings and usage records are retained in the data archive.
archive_bytes_limit:
Sets the max size in bytes for the metering archive. The archive will be trimmed to stay below this limit.
usage_metrics:
{metric name}:
Expand Down
1 change: 1 addition & 0 deletions examples/csp-billing-adapter-example-Rancher_NV.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ query_interval: 3600
reporting_api_is_cumulative: CSP_DEPENDENT_SETTING
reporting_interval: CSP_DEPENDENT_SETTING
archive_retention_period: 6
archive_bytes_limit: 1048576
usage_metrics:
managed_node_count:
consumption_reporting: volume
Expand Down
1 change: 1 addition & 0 deletions examples/csp-billing-adapter-example-SUMA.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ reporting_api_is_cumulative: CSP_DEPENDENT_SETTING
reporting_interval: CSP_DEPENDENT_SETTING
support_info_collection_cmd: CSP_DEPENDENT_SETTING
archive_retention_period: 6
archive_bytes_limit: 0
usage_metrics:
ha_systems:
consumption_reporting: volume
Expand Down
1 change: 1 addition & 0 deletions tests/data/config_bad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ query_interval: 3600
reporting_api_is_cumulative: true
reporting_interval: 3600
archive_retention_period: 6
archive_bytes_limit: 0
usage_metrics:
managed_node_count:
consumption_reporting: volume
Expand Down
1 change: 1 addition & 0 deletions tests/data/config_dimensions_gap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ query_interval: 3600
reporting_api_is_cumulative: true
reporting_interval: 3600
archive_retention_period: 6
archive_bytes_limit: 0
usage_metrics:
managed_node_count:
consumption_reporting: volume
Expand Down
1 change: 1 addition & 0 deletions tests/data/config_dimensions_no_tail.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ query_interval: 3600
reporting_api_is_cumulative: true
reporting_interval: 3600
archive_retention_period: 6
archive_bytes_limit: 0
usage_metrics:
managed_node_count:
consumption_reporting: volume
Expand Down
1 change: 1 addition & 0 deletions tests/data/config_good_average.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ query_interval: 3600
reporting_api_is_cumulative: true
reporting_interval: 3600
archive_retention_period: 6
archive_bytes_limit: 0
usage_metrics:
managed_node_count:
consumption_reporting: volume
Expand Down
1 change: 1 addition & 0 deletions tests/data/config_good_maximum.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ query_interval: 3600
reporting_api_is_cumulative: true
reporting_interval: 3600
archive_retention_period: 6
archive_bytes_limit: 0
usage_metrics:
managed_node_count:
consumption_reporting: volume
Expand Down
1 change: 1 addition & 0 deletions tests/data/config_invalid_consumption.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ query_interval: 3600
reporting_api_is_cumulative: true
reporting_interval: 3600
archive_retention_period: 6
archive_bytes_limit: 0
usage_metrics:
managed_node_count:
consumption_reporting: invalid
Expand Down
1 change: 1 addition & 0 deletions tests/data/config_no_usage_metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ query_interval: 3600
reporting_api_is_cumulative: true
reporting_interval: 3600
archive_retention_period: 6
archive_bytes_limit: 0
version: 1.1.1
1 change: 1 addition & 0 deletions tests/data/config_testing_mixed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ query_interval: 3600
reporting_api_is_cumulative: true
reporting_interval: 3600
archive_retention_period: 6
archive_bytes_limit: 0
usage_metrics:
jobs:
# test metric with average aggregation, volume reporting
Expand Down
23 changes: 22 additions & 1 deletion tests/unit/test_archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,33 @@ def test_append_metering_records():
}

for i in range(10):
archive = append_metering_records(archive, records, 6)
archive = append_metering_records(archive, records, 6, 0)

assert len(archive) == 6
assert archive[4] == records


def test_append_metering_records_max_bytes():
archive = []
records = {
"bill_time": "2024-01-03T20:06:42.076972+00:00",
"metering_id": "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF",
"usage_records": [
{
"managed_node_count": 9,
"managed_systems": 6,
"reporting_time": "2024-01-03T20:06:42.076972+00:00"
}
]
}

for i in range(4):
archive = append_metering_records(archive, records, 6, 456)

assert len(archive) == 2
assert archive[0] == records


def test_archive_record(cba_pm, cba_config):
record = {
'billing_time': '2024-02-09T18:11:59.527064+00:00',
Expand Down

0 comments on commit 0a5f1ad

Please sign in to comment.