Glaber ValueCache Programmer's Guide
Architecture Overview
The ValueCache is a sophisticated three-layer caching system implemented in C++ that provides intelligent storage and retrieval of time-series monitoring data.
Component Hierarchy
items_valuecache (glb_state_valuecache.cpp)
├── glb_valuecache_raw (raw cache layer)
│ └── glb_tsbuff_t history_cache (time-series circular buffer)
├── glb_valuecache_downsampled (aggregated cache layer)
│ └── glb_tsbuff_t* trends_cache (downsampled time-series buffer)
└── item_demand_t (demand tracking and prediction)
Core Components
1. items_valuecache Class
File: src/libs/glb_state/glb_state_valuecache.cpp/hpp
Main interface for per-item value caching. Each monitored item has its own items_valuecache instance.
Key Members
class items_valuecache {
public:
glb_valuecache_raw raw_cache; // Recent high-resolution data
glb_valuecache_downsampled downsampled_cache; // Aggregated historical data
item_demand_t demand; // Usage pattern tracking
unsigned char value_type; // ITEM_VALUE_TYPE_* constant
int last_accessed; // Last access timestamp
mem_funcs_t *memf; // Memory allocation functions
};
Main Operations
| Method | Purpose | Cache Interaction |
|---|---|---|
fetch_by_time() |
Retrieve values for time period | Reads from both caches, may trigger DB fetch |
fetch_by_count() |
Retrieve N most recent values | Primarily raw cache |
add_value() |
Insert new monitoring value | Adds to raw cache, triggers downsampling |
add_value_lld() |
Insert LLD discovery value | Raw cache only (text data) |
clean_old_values() |
Remove obsolete data | Both caches, demand-aware |
fetch_trends() |
On-demand aggregation | Computes from cached data |
2. glb_valuecache_raw Class
File: src/libs/glb_state/glb_valuecache_raw.cpp/hpp
Manages the raw, high-resolution value cache using a circular time-series buffer.
Data Structure
Uses glb_tsbuff_t - a time-sorted circular buffer with:
- Automatic growth when demand increases
- Head/tail pointers for efficient FIFO operations
- Time-based indexing for fast lookups
class glb_valuecache_raw {
private:
glb_tsbuff_t history_cache; // Circular buffer of glb_state_item_value_t
public:
int add_value(u_int64_t itemid, ZBX_DC_HISTORY *h, int now,
mem_funcs_t *memf, unsigned char value_type,
item_demand_t &demand);
int fetch_by_time(u_int64_t itemid, unsigned char value_type,
int seconds, int time_shift, int now,
zbx_vector_history_record_t *values,
item_demand_t &demand, mem_funcs_t *memf);
};
Growth Algorithm
When cache is full and demand is not met:
int calc_grow_buffer_size(int old_size) {
int new_size = old_size * 120 / 100; // Grow by 20%
if ((new_size - old_size) < 8) // Minimum growth: 8 elements
new_size = old_size + 8;
if ((new_size - old_size) > 64) // Maximum growth: 64 elements
new_size = old_size + 64;
return new_size;
}
Database Fetch Strategy
When requested data is not in cache:
- Determine fetch range: Based on requested time/count and current cache state
- Check demand: Avoid fetching less than what demand indicates
- Fetch from DB: Using
DCget_history_by_*()functions - Populate cache: Insert fetched values maintaining time order
- Update demand: Record what was fetched for future optimization
// Fetch decision logic
if (request_time_from < oldest_cached_time) {
// Need to fetch older data from DB
fetch_from_db_by_time(itemid, request_time_from, oldest_cached_time, ...);
}
if (requested_count > cached_count) {
// Need more values
fetch_from_db_by_count(itemid, requested_count, ...);
}
3. glb_valuecache_downsampled Class
File: src/libs/glb_state/glb_valuecache_downsampled.cpp/hpp
Manages aggregated data for long-term history with reduced memory footprint.
Downsampling Process
void add_data_from_raw_cache(u_int64_t itemid,
glb_valuecache_raw &raw_cache,
mem_funcs_t *memf,
unsigned char value_type,
item_demand_t &demand)
Algorithm:
1. Check if raw cache has old data beyond GLB_CACHE_ITEMS_MAX_DURATION
2. Extract values in the "exceeding" period (configurable, e.g., 1 hour)
3. Aggregate extracted values (average for numeric types)
4. Create glb_downsample_item with aggregated value
5. Add to downsampled cache
6. Values remain in raw cache until cleanup
Downsample Item Structure
class glb_downsample_item {
public:
unsigned int timestamp; // Composite: time + count
union {
u_int64_t ui64;
double dbl;
} value;
unsigned int get_count() const; // Extract count from timestamp
int get_timestamp() const; // Extract actual timestamp
};
Timestamp Encoding:
- Encodes both time and sample count in one field
- timestamp / 3600 = hour-based time
- timestamp % 3600 = count (max 3599 samples)
Lazy Initialization
Downsampled cache is created on-demand:
if (NULL == trends_cache) {
// Allocate only when first downsampled data is needed
trends_cache = allocate_new_tsbuff(...);
}
4. item_demand_t Class
File: include/objects/valuecache/item_demand.cpp/hpp
Tracks and predicts data access patterns to optimize cache allocation.
Demand Metrics
class item_demand_t {
public:
// Current active demand
int period; // Time-based demand (seconds)
int count; // Count-based demand (number of values)
int timeshift; // Time offset from current time
// Peak daily demand (learning mechanism)
int daily_period;
int daily_count;
int daily_timeshift;
// Last change timestamps
int period_change;
int count_change;
int timeshift_change;
// Database fetch tracking
int db_fetched_time_from; // Oldest time fetched from DB
int db_fetched_count; // Number of values fetched
int db_fetched_timeshift; // Timeshift used in fetch
};
Demand Update Algorithm
int update_demand(u_int64_t itemid,
unsigned int new_count,
unsigned int new_period,
unsigned int new_timeshift,
unsigned int now)
Logic: 1. Immediate Update: If new demand exceeds current, update immediately 2. Daily Learning: Track peak demands over 24 hours 3. Periodic Application: Every 24 hours, set demand to daily peak 4. Reset on Change: When demand increases, reset DB fetch tracking
Learning Period: GLB_CACHE_ITEM_DEMAND_UPDATE = 86400 seconds (24 hours)
// Example: Count demand update
if (count < new_count) {
count = new_count; // Immediate update
daily_count = 0; // Reset daily tracker
count_change = now; // Record change time
reset_db_fetch_time_and_count(); // Will need to refetch
}
// After 24 hours, apply learned demand
if (now - count_change > 86400) {
count = daily_count; // Apply learned demand
daily_count = 0; // Start new learning cycle
}
Demand Checking
Two types of demand validation:
1. Count-Based Demand:
int ensure_cache_demand_by_count_met(u_int64_t itemid, glb_tsbuff_t &tsbuf)
cache_count - 1 >= demand.count
- With timeshift: Count values between now - timeshift and oldest
2. Time-Based Demand:
int check_cache_demand_by_time_is_met(u_int64_t itemid, glb_tsbuff_t &tsbuf)
oldest_cached_time <= (now - period - timeshift)
Both must be satisfied for demand to be met.
Data Structures
glb_state_item_value_t
Core value storage structure:
struct glb_state_item_value_t {
int time_sec; // Timestamp (seconds since epoch)
union {
double dbl; // Float value (ITEM_VALUE_TYPE_FLOAT)
u_int64_t ui64; // Integer value (ITEM_VALUE_TYPE_UINT64)
char *str; // String value (TEXT/STR/LOG types)
} value;
void clear(mem_funcs_t *memf, unsigned char value_type);
void set_from_dc_history_record(mem_funcs_t *memf,
ZBX_DC_HISTORY *record,
unsigned char value_type);
void save_to_history_record(zbx_history_record_t *record,
unsigned char value_type);
};
Memory Management:
- String types: Dynamically allocated via memf->malloc_func()
- Numeric types: Stored directly in union
- Cleanup: clear() method frees string memory
glb_tsbuff_t
Time-series circular buffer (defined elsewhere in codebase):
Properties: - Fixed-size, can be resized dynamically - Head/tail pointers for FIFO operations - Time-ordered storage - Fast time-based lookups
Key Operations:
- glb_tsbuff_add_to_head(): Add newest value
- glb_tsbuff_get_value_tail(): Get oldest value
- glb_tsbuff_free_tail(): Remove oldest value
- glb_tsbuff_find_time_idx(): Binary search by timestamp
- glb_tsbuff_resize(): Grow/shrink buffer
Key Algorithms
Fetch by Time Algorithm
int items_valuecache::fetch_by_time(u_int64_t itemid, int value_type,
int seconds, int time_shift, int now,
zbx_vector_history_record_t *values,
mem_funcs_t *memf)
Flow:
1. Update Demand
└── demand.update_demand(itemid, 0, seconds, time_shift, now)
2. Fetch from Raw Cache
└── May trigger DB fetch if data missing
└── Populates both raw and downsampled caches
3. Decision Point: Is request within MAX_DURATION?
YES (recent data only):
├── Return raw cache values only
└── Return SUCCEED/FAIL
NO (extends beyond MAX_DURATION):
├── Fetch older data from downsampled cache
│ └── Calculate: downsampled_seconds = max_threshold - fetch_from
│ └── downsampled_cache.fetch_by_time(...)
├── Append raw cache values (newer data)
└── Return combined result
Example:
- now = 1000000
- GLB_CACHE_ITEMS_MAX_DURATION = 172800 (2 days)
- Request: seconds = 259200 (3 days), time_shift = 0
Request range: [740800 to 1000000]
Raw threshold: [827200 to 1000000] (within 2 days)
Downsampled: [740800 to 827200] (beyond 2 days)
Actions:
1. Fetch 172800 seconds from raw cache → [827200, 1000000]
2. Fetch 86400 seconds from downsampled → [740800, 827200]
3. Merge: downsampled values + raw values
Add Value with Downsampling
int items_valuecache::add_value(u_int64_t itemid, ZBX_DC_HISTORY *h,
int now, mem_funcs_t *memf)
Flow:
1. Value Type Check
├── If value_type changed → reset cache
└── Update value_type
2. Add to Raw Cache
└── raw_cache.add_value(itemid, h, now, memf, value_type, demand)
├── Ensure space (demand-aware)
│ ├── If full AND demand met → rotate (free tail)
│ └── If full AND demand NOT met → grow buffer
└── Insert value at head
3. Trigger Downsampling
└── downsampled_cache.add_data_from_raw_cache(...)
├── Check if raw cache has old data (> MAX_DURATION)
├── Extract values from last downsampling period
├── Aggregate (average for numeric types)
├── Create downsampled item
└── Add to downsampled cache
4. Update Access Time
└── last_accessed = now
Cleanup Algorithm
void items_valuecache::clean_old_values(u_int64_t itemid)
Two-Stage Cleanup:
Stage 1: Raw Cache Cleanup
int clean_time = now - GLB_CACHE_ITEMS_MAX_DURATION;
int clean_min_count = GLB_CACHE_ITEMS_MIN_CLEAN_COUNT;
raw_cache.clean_old_values(clean_time, clean_min_count, ...);
clean_time
- Keep at least clean_min_count values
Stage 2: Downsampled Cache Cleanup
downsampled_cache.clean_old_values(itemid, demand);
Pseudo-code:
while (cache_has_values) {
if (demand_met_without_oldest_value) {
remove_oldest_value();
} else {
break; // Stop cleanup
}
}
Trends/Aggregation Algorithm
int items_valuecache::fetch_trends(u_int64_t itemid, int value_type,
int time_from, int time_to,
int aggregation_hours, int trend_function,
zbx_vector_history_record_t *values,
std::string &error, mem_funcs_t *memf)
Purpose: Compute on-demand aggregations (hourly, daily, etc.) from cached data.
Flow:
1. Validation
├── Check value_type (only FLOAT/UINT64 supported)
├── Validate aggregation_hours > 0
└── Validate time_from < time_to
2. Exclude Current Incomplete Period
└── Adjust time_to to last complete aggregation boundary
Example: If aggregation_hours=1 and now=10:30
time_to adjusted to 10:00
3. Fetch Raw Values
└── Call fetch_by_time() to get all values in [time_from, time_to]
(May come from both raw and downsampled caches)
4. Aggregate into Buckets
├── Calculate bucket boundaries
│ first_bucket = (time_from / aggregation_seconds) * aggregation_seconds
│ num_buckets = (time_to - first_bucket) / aggregation_seconds
│
└── For each bucket:
├── Collect all values in [bucket_start, bucket_end)
├── Calculate statistics: sum, min, max, count
└── Apply trend_function:
├── AVG: sum / count
├── MIN: min_val
├── MAX: max_val
├── SUM: sum
└── COUNT: count
5. Create Result Records
└── For each non-empty bucket:
├── timestamp = bucket_start
└── value = aggregated_value
Example:
Request: Hourly averages from 2024-01-01 00:00 to 2024-01-01 05:00
aggregation_hours = 1 (3600 seconds)
Buckets:
[00:00, 01:00) → avg of values in this hour → result[0]
[01:00, 02:00) → avg of values in this hour → result[1]
[02:00, 03:00) → avg of values in this hour → result[2]
[03:00, 04:00) → avg of values in this hour → result[3]
[04:00, 05:00) → avg of values in this hour → result[4]
Serialization (Dump/Load)
Dump Format
JSON Structure:
{
"value_type": 0,
"last_accessed": 1234567890,
"demand": {
"count": 100,
"period": 3600,
"timeshift": 0
},
"raw_values": [
{"clock": 1234567890, "value": 42.5},
{"clock": 1234567891, "value": 43.2},
...
],
"downsampled_values": [
{"clock": 1234560000, "value": 40.1, "count": 60},
{"clock": 1234563600, "value": 41.3, "count": 58},
...
]
}
Dump Operation
void items_valuecache::dump_to_json(struct zbx_json *json,
unsigned char value_type)
Process: 1. Serialize metadata (value_type, last_accessed) 2. Serialize demand object 3. Serialize raw cache values (array) 4. Serialize downsampled cache values (array)
Load Operation
int items_valuecache::load_values_from_json(u_int64_t itemid,
struct zbx_json_parse *jp_valuecache,
mem_funcs_t *memf)
Process:
1. Load metadata (load_metadata())
2. Parse and load raw values
- Convert JSON to ZBX_DC_HISTORY format
- Insert into raw cache using add_value()
3. Parse and load downsampled values
- Convert JSON to downsampled items
- Insert into downsampled cache
4. Reconstruct demand state
Error Handling: - Failures in loading raw values logged but not fatal - Failures in loading downsampled values logged but not fatal - Metadata load failure is fatal
Memory Management
Memory Function Pointers
All allocations use custom memory functions:
typedef struct {
void* (*malloc_func)(void*, size_t);
void (*free_func)(void*);
void* (*realloc_func)(void*, size_t);
} mem_funcs_t;
Why?: Allows use of shared memory allocators for cache data.
String Handling
For text-based value types:
// Allocation
value.str = (char*)memf->malloc_func(NULL, strlen(source) + 1);
strcpy(value.str, source);
// Deallocation
if (value_type == ITEM_VALUE_TYPE_TEXT || ...) {
memf->free_func(value.str);
}
Important: Always check value_type before freeing/accessing string pointers.
Buffer Resizing
Raw cache growth:
int glb_tsbuff_resize(glb_tsbuff_t *tsbuf, int new_size,
void* (*malloc_func)(void*, size_t),
void (*free_func)(void*),
void *context);
- Allocates new buffer of
new_size - Copies existing values
- Frees old buffer
- Updates pointers
Configuration Constants
External Configuration (from zabbix_server.conf)
extern u_int64_t CONFIG_VALUE_CACHE_DEFAULT_ELEMENTS; // Initial buffer size
extern u_int64_t GLB_CACHE_ITEMS_MAX_DURATION; // Raw cache max age
extern u_int64_t GLB_CACHE_ITEMS_MIN_CLEAN_COUNT; // Min values to keep
extern u_int64_t GLB_DOWNSAMPLE_PERIOD; // Downsampling interval
Internal Constants
#define CACHE_RESERVE_PERCENT 10 // Buffer reserve capacity
#define CACHE_RESERVE_COUNT 32 // Minimum reserve slots
#define GLB_CACHE_ITEM_DEMAND_UPDATE 86400 // Demand learning period (24h)
Trend Functions
#define TREND_FUNCTION_AVG 0
#define TREND_FUNCTION_MIN 1
#define TREND_FUNCTION_MAX 2
#define TREND_FUNCTION_SUM 3
#define TREND_FUNCTION_COUNT 4
Threading and Concurrency
Important: The code shown does not include explicit locking. Concurrency control is expected to be handled at a higher level (e.g., in the item management layer).
Assumptions:
- Per-item operations are serialized by caller
- Shared memory access is protected externally
- mem_funcs_t operations are thread-safe
Recommendation: When integrating, ensure:
1. Each item's valuecache accessed by single thread at a time
2. Or, add mutex protection around all public methods
3. Memory allocator (memf) is thread-safe
Error Handling
Return Codes
SUCCEED(0): Operation successfulFAIL(typically -1): Operation failed
Error Scenarios
| Scenario | Return | Action |
|---|---|---|
| Cache miss (no data) | FAIL |
Trigger DB fetch |
| DB fetch fails | FAIL |
Log error, return empty result |
| Value type mismatch | FAIL |
Reset cache with new type |
| Invalid parameters | FAIL |
Log error, return |
| Memory allocation fails | FAIL |
Log critical error |
Logging
Uses Glaber debug macros:
DEBUG_ITEM(itemid, "Message format %d", value);
LOG_INF("Information message");
Enable detailed logging for specific items by setting debug level in configuration.
Performance Considerations
Optimization Techniques
- Circular Buffers: O(1) add/remove operations
- Time-Based Indexing: Binary search for time lookups
- Lazy Downsampling: Only aggregate when beneficial
- Demand-Based Allocation: Memory used only where needed
- Database Fetch Coalescing: Fetch once, cache for multiple queries
Cache Hit Optimization
To maximize cache hits:
1. Set ValueCacheMaxDuration to cover typical query ranges
2. Allow 24-hour learning period for demand tracking
3. Pre-warm cache by querying historical data
4. Monitor and tune based on access patterns
Memory Efficiency
Downsampling reduces memory by: - Aggregating multiple values into one - Storing only timestamp + aggregated value + count - Typically 10-100x compression for old data
Example: - Raw: 3600 values × 16 bytes = 57.6 KB per hour - Downsampled: 1 value × 16 bytes = 16 bytes per hour - Compression: ~3600:1
Integration Points
Item Management Layer
The valuecache integrates with the item management system:
// In item lifecycle
item_init() → valuecache.init(memf)
item_add_value() → valuecache.add_value(...)
item_fetch_history() → valuecache.fetch_by_time(...)
item_destroy() → valuecache.destroy(memf)
Database Backend
Cache misses trigger database fetches via:
- DCget_history_by_time(): Fetch values in time range
- DCget_history_by_count(): Fetch N most recent values
These functions must be implemented in the database layer.
Configuration System
Read configuration during initialization:
GLB_CACHE_ITEMS_MAX_DURATION = read_config("ValueCacheMaxDuration", default);
// etc.
Testing Strategies
Unit Testing
Test individual components in isolation:
- Demand Tracking:
- Test demand updates with various patterns
- Verify 24-hour learning cycle
-
Check demand satisfaction logic
-
Raw Cache:
- Test buffer growth/shrink
- Verify cleanup with demand
-
Test time/count-based fetches
-
Downsampled Cache:
- Test aggregation correctness
- Verify composite timestamp encoding
- Test demand-based cleanup
Integration Testing
Test component interactions:
- Add → Downsample → Fetch:
- Add values over time
- Verify automatic downsampling
-
Fetch mixed raw+downsampled data
-
Demand-Based Growth:
- Simulate increasing demand
- Verify buffer grows appropriately
-
Check cleanup doesn't violate demand
-
Dump → Restart → Load:
- Dump cache state to JSON
- Clear cache
- Load from JSON
- Verify identical state
Performance Testing
Benchmark key operations:
// Example benchmark
for (int i = 0; i < 10000; i++) {
valuecache.add_value(itemid, &history, now + i, memf);
}
// Measure: throughput, memory usage, cache hit rate
for (int i = 0; i < 1000; i++) {
valuecache.fetch_by_time(itemid, value_type, 3600, 0, now, &values, memf);
}
// Measure: latency, cache hits vs DB fetches
Future Enhancements
Potential Improvements
- Compression: Compress old data before downsampling
- Adaptive Downsampling: Variable aggregation periods based on data characteristics
- Predictive Fetching: Prefetch likely-needed data from DB
- Lock-Free Structures: Improve concurrency with lock-free data structures
- Tiered Downsampling: Multiple downsampling levels (hourly → daily → weekly)
Extensibility Points
To extend the valuecache:
- New Aggregation Functions:
- Add to
TREND_FUNCTION_*constants -
Implement in
fetch_trends()switch statement -
Custom Value Types:
- Extend
glb_state_item_value_tunion -
Update
clear(),set_from_*(),save_to_*()methods -
Alternative Storage:
- Replace
glb_tsbuff_twith custom structure - Implement same interface (add, get, resize, etc.)
Common Pitfalls
1. Memory Leaks with Strings
Problem: Forgetting to free string values
// BAD
glb_tsbuff_free_tail(&cache); // Leaks string memory!
// GOOD
glb_state_item_value_t *val = (glb_state_item_value_t*)glb_tsbuff_get_value_tail(&cache);
val->clear(memf, value_type); // Frees string first
glb_tsbuff_free_tail(&cache);
2. Value Type Mismatches
Problem: Accessing wrong union member
// BAD
if (value_type == ITEM_VALUE_TYPE_FLOAT) {
printf("%llu", val->value.ui64); // Wrong member!
}
// GOOD
if (value_type == ITEM_VALUE_TYPE_FLOAT) {
printf("%f", val->value.dbl);
}
3. Ignoring Demand Updates
Problem: Not updating demand on fetches
// BAD
int fetch_by_time(...) {
// Forgot to update demand!
return raw_cache.fetch_by_time(...);
}
// GOOD
int fetch_by_time(...) {
demand.update_demand(itemid, 0, seconds, time_shift, now);
return raw_cache.fetch_by_time(...);
}
4. Incomplete Cleanup
Problem: Cleaning raw cache but not downsampled
// BAD
void clean_old_values() {
raw_cache.clean_old_values(...);
// Forgot downsampled cache!
}
// GOOD
void clean_old_values() {
raw_cache.clean_old_values(...);
downsampled_cache.clean_old_values(...);
}
Code Examples
Complete Fetch Example
// Fetch last hour of data for an item
u_int64_t itemid = 12345;
int value_type = ITEM_VALUE_TYPE_FLOAT;
int seconds = 3600; // 1 hour
int time_shift = 0; // No time shift
int now = glb_time(NULL);
zbx_vector_history_record_t values;
zbx_history_record_vector_create(&values);
// Fetch from cache (may trigger DB fetch)
int ret = valuecache.fetch_by_time(itemid, value_type, seconds,
time_shift, now, &values, memf);
if (ret == SUCCEED) {
// Process values
for (int i = 0; i < values.values_num; i++) {
printf("Time: %d, Value: %f\n",
values.values[i].timestamp.sec,
values.values[i].value.dbl);
}
}
// Cleanup
zbx_history_record_vector_destroy(&values, value_type);
Complete Add Example
// Add a new monitoring value
ZBX_DC_HISTORY history;
history.itemid = 12345;
history.metric.ts.sec = glb_time(NULL);
history.metric.ts.ns = 0;
history.hist_value_type = ITEM_VALUE_TYPE_FLOAT;
history.metric.value.data.dbl = 42.5;
int now = glb_time(NULL);
// Add to cache (triggers downsampling if needed)
int ret = valuecache.add_value(itemid, &history, now, memf);
if (ret == SUCCEED) {
printf("Value added to cache\n");
} else {
printf("Failed to add value\n");
}
Complete Trends Example
// Get hourly averages for last 24 hours
u_int64_t itemid = 12345;
int value_type = ITEM_VALUE_TYPE_FLOAT;
int now = glb_time(NULL);
int time_from = now - 86400; // 24 hours ago
int time_to = now;
int aggregation_hours = 1; // 1-hour buckets
int trend_function = TREND_FUNCTION_AVG;
zbx_vector_history_record_t trends;
zbx_history_record_vector_create(&trends);
std::string error;
// Compute trends from cached data
int ret = valuecache.fetch_trends(itemid, value_type, time_from, time_to,
aggregation_hours, trend_function,
&trends, error, memf);
if (ret == SUCCEED) {
printf("Generated %d hourly averages\n", trends.values_num);
for (int i = 0; i < trends.values_num; i++) {
printf("Hour starting %d: avg = %f\n",
trends.values[i].timestamp.sec,
trends.values[i].value.dbl);
}
} else {
printf("Failed to compute trends: %s\n", error.c_str());
}
zbx_history_record_vector_destroy(&trends, value_type);
Debugging
Enable Debug Logging
In configuration:
DebugLevel=4
Or programmatically:
#define DEBUG_ITEM_ENABLED
Key Debug Messages
Look for these patterns:
"Fetching %d seconds from raw cache"
"Cache demand IS MET/IS NOT MET without last item"
"Downsampled cache added %d values"
"Resizing cache %d->%d"
"DB fetch completed: %d values"
Memory Inspection
Check cache statistics:
int count = valuecache.get_cache_count();
int size = valuecache.get_cache_size();
printf("Cache: %d values, %d bytes\n", count, size);
// Inspect demand
printf("Demand: count=%d, period=%d, timeshift=%d\n",
valuecache.demand.count,
valuecache.demand.period,
valuecache.demand.timeshift);
Dump Cache State
struct zbx_json json;
zbx_json_init(&json, 1024);
valuecache.dump_to_json(&json, value_type);
printf("%s\n", json.buffer);
zbx_json_free(&json);
Summary
The Glaber ValueCache is a sophisticated, adaptive caching system that:
- Intelligently manages memory based on actual usage patterns
- Optimizes performance through two-tier caching (raw + downsampled)
- Minimizes database load by learning and predicting data needs
- Provides persistence through JSON serialization
- Supports trends with on-demand aggregation
The modular design allows easy extension and testing, while the demand-based approach ensures efficient resource utilization in production environments.