class Metric
A cached metric reference that avoids hash lookups on the fast path.
This class caches all the details needed to write directly to shared memory, including the buffer, offset, and type. When the observer changes, the cache is invalidated and rebuilt on the next access.
Definitions
def initialize(name, registry)
Initialize a new metric.
Signature
-
parameter
nameSymbol The field name for this metric.
-
parameter
registryRegistry The registry instance to use.
Implementation
def initialize(name, registry)
@name = name.to_sym
@registry = registry
@value = 0
@cache_valid = false
@cached_field_info = nil
@cached_buffer = nil
@guard = Mutex.new
end
attr :name
Signature
-
attribute
Symbol The field name for this metric.
attr :value
Signature
-
attribute
Numeric The current value of this metric.
attr :guard
Signature
-
attribute
Mutex The mutex for thread safety.
def invalidate
Invalidate the cached field information.
Called when the observer changes to force cache rebuild.
Implementation
def invalidate
@cache_valid = false
@cached_field_info = nil
@cached_buffer = nil
end
def increment(&block)
Increment the metric value, optionally with a block that auto-decrements.
Uses the fast path (direct buffer write) when cache is valid and observer is available.
Signature
-
returns
Integer The new value of the field.
Implementation
def increment(&block)
@guard.synchronize do
@value += 1
write_direct(@value)
end
if block_given?
begin
yield
ensure
# Decrement after block completes
decrement
end
end
@value
end
def decrement
Decrement the metric value.
Uses the fast path (direct buffer write) when cache is valid and observer is available.
Signature
-
returns
Integer The new value of the field.
Implementation
def decrement
@guard.synchronize do
@value -= 1
write_direct(@value)
end
@value
end
def set(value)
Set the metric value.
Uses the fast path (direct buffer write) when cache is valid and observer is available.
Signature
-
parameter
valueNumeric The value to set.
Implementation
def set(value)
@guard.synchronize do
@value = value
write_direct(@value)
end
end
def ensure_cache_valid!
Check if the cache is valid and rebuild if necessary.
Always attempts to build the cache if it's invalid. Returns true if cache is now valid (observer exists, field is in schema, and buffer is available), false otherwise.
Signature
-
returns
bool True if cache is valid, false otherwise.
Implementation
def ensure_cache_valid!
unless @cache_valid
if observer = @registry.observer
if field = observer.schema[@name]
if buffer = observer.buffer
@cached_field_info = field
@cached_buffer = buffer
end
end
end
# Once we've validated the cache, even if there was no observer or buffer, we mark it as valid, so that we don't try to revalidate it again:
@cache_valid = true
end
end
def write_direct(value)
Write directly to the cached buffer if available.
This is the fast path that avoids hash lookups. Always ensures cache is valid first. If there's no observer or buffer, silently does nothing.
Signature
-
parameter
valueNumeric The value to write.
-
returns
Boolean Whether the write succeeded.
Implementation
def write_direct(value)
self.ensure_cache_valid!
if @cached_buffer
@cached_buffer.set_value(@cached_field_info.type, @cached_field_info.offset, value)
end
return true
rescue => error
# If write fails, log warning but don't invalidate cache
# The error might be transient, and invalidating would force hash lookups
Console.warn(self, "Failed to write metric value!", metric: {name: @name, value: value}, exception: error)
return false
end