From 7740389e9e9a136f96839ed1a3bb53e26430c0d1 Mon Sep 17 00:00:00 2001 From: water <672684719@qq.com> Date: Thu, 27 Aug 2026 19:39:45 +0800 Subject: [PATCH 1/3] Add SingleNodeCuratorCacheStorage using AtomicReference for single-node caches --- .../cache/SingleNodeCuratorCacheStorage.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/SingleNodeCuratorCacheStorage.java diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/SingleNodeCuratorCacheStorage.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/SingleNodeCuratorCacheStorage.java new file mode 100644 index 000000000..ff92e5161 --- /dev/null +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/SingleNodeCuratorCacheStorage.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.curator.framework.recipes.cache; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +/** + * Storage implementation optimized for a {@link CuratorCache} created with the + * {@link CuratorCache.Options#SINGLE_NODE_CACHE} option. In that mode only a + * single node is cached, so a single {@link AtomicReference} is sufficient + * instead of a {@link java.util.concurrent.ConcurrentHashMap}. + */ +class SingleNodeCuratorCacheStorage implements CuratorCacheStorage { + private final AtomicReference data = new AtomicReference<>(null); + private final boolean cacheBytes; + + SingleNodeCuratorCacheStorage(boolean cacheBytes) { + this.cacheBytes = cacheBytes; + } + + @Override + public Optional put(ChildData childData) { + ChildData localData = cacheBytes ? childData : new ChildData(childData.getPath(), childData.getStat(), null); + return Optional.ofNullable(data.getAndSet(localData)); + } + + @Override + public Optional remove(String path) { + return Optional.ofNullable(data.getAndUpdate(current -> current != null && current.getPath().equals(path) ? null : current)); + } + + @Override + public Optional get(String path) { + ChildData childData = data.get(); + return (childData != null && childData.getPath().equals(path)) ? Optional.of(childData) : Optional.empty(); + } + + @Override + public int size() { + return (data.get() != null) ? 1 : 0; + } + + @Override + public Stream stream() { + ChildData childData = data.get(); + return (childData != null) ? Stream.of(childData) : Stream.empty(); + } + + @Override + public void clear() { + data.set(null); + } +} From 1b6b274a5b4ae04c852bd9f0f42a046bfcd81532 Mon Sep 17 00:00:00 2001 From: water <672684719@qq.com> Date: Thu, 27 Aug 2026 19:39:46 +0800 Subject: [PATCH 2/3] Add singleNode() storage factory methods --- .../recipes/cache/CuratorCacheStorage.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheStorage.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheStorage.java index 3648503ae..4ecc6d041 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheStorage.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheStorage.java @@ -35,6 +35,28 @@ static CuratorCacheStorage standard() { return new StandardCuratorCacheStorage(true); } + /** + * Return a new storage instance optimized for a single-node cache + * (i.e. a {@link CuratorCache} built with {@link CuratorCache.Options#SINGLE_NODE_CACHE}). + * Only a single node is stored, so a lightweight {@link java.util.concurrent.atomic.AtomicReference} + * is used instead of a concurrent map. + * + * @return single-node storage instance + */ + static CuratorCacheStorage singleNode() { + return new SingleNodeCuratorCacheStorage(true); + } + + /** + * Return a new single-node storage instance that does not retain the data bytes, i.e. ChildData + * objects returned by this storage will always return {@code null} for {@link ChildData#getData()}. + * + * @return single-node storage instance that does not retain data bytes + */ + static CuratorCacheStorage singleNodeDataNotCached() { + return new SingleNodeCuratorCacheStorage(false); + } + /** * Return a new storage instance that does not retain the data bytes. i.e. ChildData objects * returned by this storage will always return {@code null} for {@link ChildData#getData()}. From eae5d97e9f2383a0f4068cb3bc0799768c4eddd8 Mon Sep 17 00:00:00 2001 From: water <672684719@qq.com> Date: Thu, 27 Aug 2026 19:39:48 +0800 Subject: [PATCH 3/3] Use single-node storage when SINGLE_NODE_CACHE option is set --- .../curator/framework/recipes/cache/CuratorCacheImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheImpl.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheImpl.java index 23268f5e6..5d6dae576 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheImpl.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheImpl.java @@ -77,9 +77,9 @@ private enum State { Consumer exceptionHandler) { Set options = (optionsArg != null) ? Sets.newHashSet(optionsArg) : Collections.emptySet(); this.client = client; - this.storage = (storage != null) ? storage : CuratorCacheStorage.standard(); - this.path = path; recursive = !options.contains(Options.SINGLE_NODE_CACHE); + this.storage = (storage != null) ? storage : (recursive ? CuratorCacheStorage.standard() : CuratorCacheStorage.singleNode()); + this.path = path; compressedData = options.contains(Options.COMPRESSED_DATA); clearOnClose = !options.contains(Options.DO_NOT_CLEAR_ON_CLOSE); persistentWatcher = new PersistentWatcher(client, path, recursive);