public MetadataLoader build() {
            if (logContext == null) {
                logContext = new LogContext("[MetadataLoader id=" + nodeId + "] ");
            }
            if (highWaterMarkAccessor == null) {
                throw new RuntimeException("You must set the high water mark accessor.");
            }
            if (metrics == null) {
                metrics = new MetadataLoaderMetrics(Optional.empty(),
                    __ -> { },
                    __ -> { },
                    new AtomicReference<>(MetadataProvenance.EMPTY));
            }
            return new MetadataLoader(
                time,
                logContext,
                nodeId,
                threadNamePrefix,
                faultHandler,
                metrics,
                highWaterMarkAccessor);
        }
The MetadataLoader follows changes provided by a RaftClient, and packages them into metadata
deltas and images that can be consumed by publishers.

The Loader maintains its own thread, which is used to make all callbacks into publishers. If a
publisher A is installed before B, A will receive all callbacks before B. This is also true if
A and B are installed as part of a list [A, B].

Publishers should not modify any data structures passed to them.

It is possible to change the list of publishers dynamically over time. Whenever a new publisher is
added, it receives a catch-up delta which contains the full state. Any publisher installed when the
loader is closed will itself be closed.
metadata/src/main/java/org/apache/kafka/image/loader/MetadataLoader.java