Frequently Asked Questions

Everything you need to know about MARZ.

Why not just use rolling restarts?

Rolling restarts work, but they’re a sledgehammer for a thumbtack. A boolean toggle shouldn’t take 15-45 minutes of deployment pipeline time. Rolling restarts also drain connection pools, reset circuit breakers, invalidate caches, and require change tickets in regulated environments. MARZ does it in ~5ms with zero state loss. For the 20% of config changes that involve connection strings, pool sizes, or TLS certs — keep using restarts. MARZ handles the other 80%.

How is this different from Spring Cloud Config / @RefreshScope?

@RefreshScope destroys and recreates the entire bean. @Marz updates one field. @RefreshScope wraps every method call in a CGLIB proxy (~50-200ns overhead per call). @Marz is a direct volatile field read (~31ns, zero proxy). @RefreshScope requires a Config Server, actuator endpoint, and optionally RabbitMQ/Kafka. @Marz needs nothing — it reads local files via OS events. @RefreshScope invalidates all refresh-scoped beans on every refresh, even unchanged ones. @Marz touches only the field(s) bound to the changed key.

How is this different from LaunchDarkly?

LaunchDarkly’s SDK makes a network call on every flag check. MARZ reads a volatile field (~31ns, zero network). LaunchDarkly charges $20K-$120K/year for enterprise. MARZ’s open-source library is free. LaunchDarkly is language-agnostic; MARZ is Java/Spring-native. If LaunchDarkly’s service goes down, your flags may revert to defaults. MARZ’s values are in memory and persisted to a local file — no external dependency for runtime operation.

Why must fields be volatile?

Without volatile, the Java Memory Model does not guarantee that a write from the WatchService thread is visible to your request-handling thread. You could read a stale value indefinitely. volatile provides a formal happens-before guarantee (JLS §17.4.5). MARZ validates this at startup — if you forget volatile, the application fails fast with a clear error message.

What happens if the config file doesn't exist?

MARZ creates it with all @Marz default values. Your application starts normally, and a complete config file materializes — ready to edit.

What happens if a key is missing from the config file?

MARZ uses the defaultValue from the annotation and writes the missing key to the file at startup (self-registration). The next time you open the file, every configurable field is visible.

What types are supported?

boolean, int, long, double, and String. All must be declared volatile.

Can I use this with Kubernetes ConfigMaps?

Yes. Mount the ConfigMap as a directory and point @Marz at the file inside it (source = "file:///etc/marz/marz.yml"). Kubernetes updates ConfigMap volumes by atomically swapping a ..data directory symlink rather than rewriting the file; MARZ detects that swap and re-reads through the symlink, so changes apply within the debounce window — no restart, no waiting on the safety-net poll.

One caveat: a ConfigMap mounted with subPath is copied once at pod start and never updated by Kubernetes — that’s a platform limitation no tool can work around. Mount the ConfigMap as a directory (not subPath) so live updates propagate. MARZ logs the resolved mount mode (KUBERNETES_CONFIGMAP vs PLAIN_FILE) at startup.

What about sensitive values like API keys?

Set sensitive = true on the annotation: @Marz(key = "api.key", sensitive = true). Values are masked as *** in all log output, Spring events, and platform heartbeats.

Does this work without the MARZ Platform?

Yes. The open-source library works standalone with file-based config. The platform is an optional upgrade for dashboard visibility, RBAC, and multi-instance management. No vendor lock-in.

What can MARZ NOT do?

MARZ cannot reconfigure connection pools, thread pools, data sources, TLS certificates, or anything computed at @PostConstruct / startup. Those need a restart. MARZ handles runtime variables — feature flags, rate limits, display strings, experiment cohorts. The 80% of config changes that are simple value toggles.

What's the performance overhead?

Read path: ~31ns (a plain volatile field read — no IO, no reflection, no proxy), measured over 1,000,000 reads (~32M ops/sec) on Java 17. Idle: zero IO (WatchService blocks on OS kernel events). Write: ~0.5ms per change (parse + diff + volatile write, few times per day). The library adds effectively zero overhead to your application’s hot path.

Is it thread-safe?

Yes. volatile provides JMM happens-before guarantees (JLS §17.4.5). Writes to boolean, int, and object references are atomic (JLS §17.7). CAS deduplication prevents duplicate events. No locking. No blocking. Readers never wait for writers.

What Java / Spring Boot versions are supported?

Java 17+ and Spring Boot 3.x. The library uses the java.nio.file.WatchService API (available since Java 7) and Spring Boot 3’s auto-configuration model.

How do I listen for config changes in my code?

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());
}
What's the MARZ Platform?

A commercial SaaS dashboard (coming soon) that provides centralized visibility, RBAC-controlled toggle management, audit trails, and real-time propagation status across all running instances. The annotation library is free and open-source. The platform is the commercial upgrade for teams that need operational control.