How It Works
Read Path (~31ns)
Application code reads the volatile field directly. This compiles to a MOV instruction + memory barrier. No proxy. No method call. No IO. No reflection. This is the hot path — measured at ~31ns/op over 1,000,000 reads (~32M ops/sec).
Write Path (event-driven)
When a config file changes:
- OS WatchService detects the file modification (~50ms)
- MARZ reads and parses the file
- Diff against cached state — only changed keys are identified
- For each changed key, the reverse index finds the bound field(s)
- CAS deduplication prevents duplicate events (vim double-write pattern)
- Volatile
field.set(bean, newValue)— immediately visible to all threads MarzEventpublished to Spring’sApplicationEventPublisher
Why volatile?
Every @Marz field must be declared volatile. This isn’t optional — MARZ validates at startup and fails fast with a clear error if you forget. The volatile keyword provides a Java Memory Model (JMM) happens-before guarantee: when the WatchService thread writes a new value, every application thread sees it on their next read. ~31ns measured cost. Zero locking. Zero blocking.
Self-Registration
If a @Marz field’s key doesn’t exist in the config file, MARZ writes it with the defaultValue at startup. Your config file becomes self-documenting — every @Marz field in the application is visible in one file without reading source code.
Listening for Changes
MARZ publishes a MarzEvent to Spring’s event system on every change:
@EventListener
public void onConfigChange(MarzEvent event) {
log.info("Key '{}' changed from {} to {}",
event.getKey(), event.getOldValue(), event.getNewValue());
}