This seemed like a honking great idea at first. Compound fields would look just like a mini-MmStat model:
class SamplingCounterField(CompoundField):
"""Records increments per ms every N increments"""
counter = CounterField()
per_ms = UInt64Field()
class _Counter(object):
"""Implement counter/rate-sampling logic here"""
def __get__(self, inst, owner):
if inst is None:
return self
return inst._fields[self.key]._counter_instance
The blocker is that there’s no way to atomically update all of the compound fields. The only way to accomplish this is for compound fields to appear as a single double buffered field with each component field as a type in the type signature:
class SamplingCounterField(DoubleBufferedField):
initial = (
CounterField.initial,
UInt64Field.initial,
)
buffer_type = (
CounterField.buffer_type,
UInt64Field.buffer_type,
)
type_signature = (
CounterField.type_signature + UInt64Field.type_signature
)
Obviously an actual implementation should remove the redundant references to the component types.
Note: Lack of atomicity is not a blocker for exposing fields such as Mean, Median, and Percentiles.
Solution: Future versions of the mmstats format should support structs as values instead of just scalars so that a single write buffer offset can point to multiple values.
To get around having to dynamically creating the structs due to a variable label size, put the labels in a header index with a pointer to the actual struct field.
Store metadata seperate from values. Then store values in multiple pages and flip between pages for read/write buffering.