dlmarion commented on a change in pull request #2569: URL: https://github.com/apache/accumulo/pull/2569#discussion_r828321261
########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/PropCacheId.java ########## @@ -0,0 +1,278 @@ +/* + * 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.accumulo.server.conf.store; + +import static org.apache.accumulo.core.Constants.ZCONFIG; +import static org.apache.accumulo.core.Constants.ZNAMESPACES; +import static org.apache.accumulo.core.Constants.ZNAMESPACE_CONF; +import static org.apache.accumulo.core.Constants.ZTABLES; +import static org.apache.accumulo.core.Constants.ZTABLE_CONF; + +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; + +import org.apache.accumulo.core.data.InstanceId; +import org.apache.accumulo.core.data.NamespaceId; +import org.apache.accumulo.core.data.TableId; +import org.apache.accumulo.fate.zookeeper.ZooUtil; +import org.apache.accumulo.server.ServerContext; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Provides a strongly-typed id for storing properties in ZooKeeper. The path in ZooKeeper is + * determined by the instance id and the type (system, namespace and table), with different root + * paths. + * <p> + * Provides utility methods from constructing different id based on type and methods to parse a + * ZooKeeper path and return a prop cache id. + */ +public class PropCacheId implements Comparable<PropCacheId> { Review comment: Should / could this extend AbstractId ? ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/impl/ZooPropStore.java ########## @@ -0,0 +1,584 @@ +/* + * 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.accumulo.server.conf.store.impl; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; + +import org.apache.accumulo.core.data.InstanceId; +import org.apache.accumulo.core.metrics.MetricsUtil; +import org.apache.accumulo.fate.zookeeper.ZooReaderWriter; +import org.apache.accumulo.fate.zookeeper.ZooUtil; +import org.apache.accumulo.server.ServerContext; +import org.apache.accumulo.server.conf.codec.VersionedPropCodec; +import org.apache.accumulo.server.conf.codec.VersionedPropGzipCodec; +import org.apache.accumulo.server.conf.codec.VersionedProperties; +import org.apache.accumulo.server.conf.store.PropCache; +import org.apache.accumulo.server.conf.store.PropCacheId; +import org.apache.accumulo.server.conf.store.PropChangeListener; +import org.apache.accumulo.server.conf.store.PropStore; +import org.apache.accumulo.server.conf.store.PropStoreException; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.data.Stat; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.benmanes.caffeine.cache.Ticker; +import com.google.common.annotations.VisibleForTesting; + +public class ZooPropStore implements PropStore, PropChangeListener { + + private final static Logger log = LoggerFactory.getLogger(ZooPropStore.class); + private final static VersionedPropCodec codec = VersionedPropGzipCodec.codec(true); + + private final ZooReaderWriter zrw; + private final PropStoreWatcher propStoreWatcher; + private final PropCache cache; + private final PropStoreMetrics cacheMetrics = new PropStoreMetrics(); + private final ReadyMonitor zkReadyMon; + + /** + * Create instance using ZooPropStore.Builder + * + * @param instanceId + * the instance id + * @param zrw + * a wrapper set of utilities for accessing ZooKeeper. + * @param readyMonitor + * coordination utility for ZooKeeper connection status. + * @param propStoreWatcher + * an extended ZooKeeper watcher + * @param ticker + * a synthetic clock used for testing. + */ + private ZooPropStore(final InstanceId instanceId, final ZooReaderWriter zrw, + final ReadyMonitor readyMonitor, final PropStoreWatcher propStoreWatcher, + final Ticker ticker) { + + this.zrw = zrw; + this.zkReadyMon = readyMonitor; + this.propStoreWatcher = propStoreWatcher; + + MetricsUtil.initializeProducers(cacheMetrics); + + ZooPropLoader propLoader = new ZooPropLoader(zrw, codec, propStoreWatcher, cacheMetrics); + + if (ticker == null) { + cache = new CaffeineCache.Builder(propLoader, cacheMetrics).build(); + } else { + cache = new CaffeineCache.Builder(propLoader, cacheMetrics).withTicker(ticker).build(); + } + + try { + var path = ZooUtil.getRoot(instanceId); + if (zrw.exists(path, propStoreWatcher)) { + log.debug("Have a ZooKeeper connection and found instance node: {}", instanceId); + zkReadyMon.setReady(); + } else { + throw new IllegalStateException("Instance may not have been initialized, root node: " + path + + " does not exist in ZooKeeper"); + } + } catch (InterruptedException | KeeperException ex) { + throw new IllegalStateException("Failed to read root node " + instanceId + " from ZooKeeper", + ex); + } + } + + public static VersionedPropCodec getCodec() { + return codec; + } + + public static PropStore initialize(final InstanceId instanceId, final ZooReaderWriter zrw) { + return new ZooPropStore.Builder(instanceId, zrw, zrw.getSessionTimeout()).build(); Review comment: Is it envisioned that multiple ZooPropStore instances can be created in the VM? If not, is there a reason why we aren't using the singleton pattern here? ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/impl/CaffeineCache.java ########## @@ -0,0 +1,144 @@ +/* + * 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.accumulo.server.conf.store.impl; + +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +import org.apache.accumulo.core.util.threads.ThreadPools; +import org.apache.accumulo.server.conf.codec.VersionedProperties; +import org.apache.accumulo.server.conf.store.PropCache; +import org.apache.accumulo.server.conf.store.PropCacheId; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.benmanes.caffeine.cache.CacheLoader; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.github.benmanes.caffeine.cache.RemovalCause; +import com.github.benmanes.caffeine.cache.Ticker; +import com.google.common.annotations.VisibleForTesting; + +public class CaffeineCache implements PropCache { Review comment: Rename to PropCacheImpl? ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/impl/PropStoreMetrics.java ########## @@ -0,0 +1,92 @@ +/* + * 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.accumulo.server.conf.store.impl; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import org.apache.accumulo.core.metrics.MetricsProducer; +import org.apache.accumulo.core.metrics.MetricsUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; + +public class PropStoreMetrics implements MetricsProducer { + + private static final Logger log = LoggerFactory.getLogger(PropStoreMetrics.class); + + private Timer load; + private Counter refresh; + private Counter refreshLoad; + private Counter eviction; + private Counter zkError; + + @Override + public void registerMetrics(MeterRegistry registry) { + + load = Timer.builder(METRICS_PROPSTORE_LOAD_TIMER).description("prop store load time") + .tags(MetricsUtil.getCommonTags()).register(registry); + + refresh = + Counter.builder(METRICS_PROPSTORE_REFRESH_COUNT).description("prop store refresh count") + .tags(MetricsUtil.getCommonTags()).register(registry); + + refreshLoad = Counter.builder(METRICS_PROPSTORE_REFRESH_LOAD_COUNT) + .description("prop store refresh load count").tags(MetricsUtil.getCommonTags()) + .register(registry); + + eviction = + Counter.builder(METRICS_PROPSTORE_EVICTION_COUNT).description("prop store eviction count") + .tags(MetricsUtil.getCommonTags()).register(registry); + + zkError = Counter.builder(METRICS_PROPSTORE_ZK_ERROR_COUNT) + .description("prop store ZooKeeper error count").tags(MetricsUtil.getCommonTags()) + .register(registry); + + } + + public PropStoreMetrics() { + log.info("Creating PropStore metrics"); Review comment: set to debug? ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/PropCacheId.java ########## @@ -0,0 +1,278 @@ +/* + * 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.accumulo.server.conf.store; + +import static org.apache.accumulo.core.Constants.ZCONFIG; +import static org.apache.accumulo.core.Constants.ZNAMESPACES; +import static org.apache.accumulo.core.Constants.ZNAMESPACE_CONF; +import static org.apache.accumulo.core.Constants.ZTABLES; +import static org.apache.accumulo.core.Constants.ZTABLE_CONF; + +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; + +import org.apache.accumulo.core.data.InstanceId; +import org.apache.accumulo.core.data.NamespaceId; +import org.apache.accumulo.core.data.TableId; +import org.apache.accumulo.fate.zookeeper.ZooUtil; +import org.apache.accumulo.server.ServerContext; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Provides a strongly-typed id for storing properties in ZooKeeper. The path in ZooKeeper is + * determined by the instance id and the type (system, namespace and table), with different root + * paths. + * <p> + * Provides utility methods from constructing different id based on type and methods to parse a + * ZooKeeper path and return a prop cache id. + */ +public class PropCacheId implements Comparable<PropCacheId> { + + public static final String PROP_NODE_NAME = "encoded_props"; + + // indices for path.split(); + public static final int TYPE_TOKEN_POSITION = 3; + public static final int IID_TOKEN_POSITION = 2; + public static final int ID_TOKEN_POSITION = 4; + + // remove starting slash from constant. + public static final String TABLES_NODE_NAME = ZTABLES.substring(1); + public static final String NAMESPACE_NODE_NAME = ZNAMESPACES.substring(1); + + private final String path; + private final IdType idType; + private final NamespaceId namespaceId; + private final TableId tableId; + + private PropCacheId(final String path, final IdType idType, final NamespaceId namespaceId, + final TableId tableId) { + this.path = path; + this.idType = idType; + this.namespaceId = namespaceId; + this.tableId = tableId; + } + + /** + * Instantiate a system prop cache id using the instance id from the context. + * + * @param context + * the system context specifying the instance id + * @return a prop cache id for system properties, + */ + public static PropCacheId forSystem(final ServerContext context) { + return forSystem(context.getInstanceID()); + } + + /** + * Instantiate a system prop cache id. + * + * @param instanceId + * the instance id. + * @return a prop cache id for system properties, + */ + public static PropCacheId forSystem(final InstanceId instanceId) { + return new PropCacheId(ZooUtil.getRoot(instanceId) + ZCONFIG + "/" + PROP_NODE_NAME, Review comment: Would it make sense to cache newly create system / namespace / table PropCacheId objects and return the cached instance instead of creating new ones? ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/impl/PropStoreWatcher.java ########## @@ -0,0 +1,253 @@ +/* + * 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.accumulo.server.conf.store.impl; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; + +import org.apache.accumulo.core.util.threads.ThreadPools; +import org.apache.accumulo.server.conf.store.PropCacheId; +import org.apache.accumulo.server.conf.store.PropChangeListener; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class serves as a translator between ZooKeeper events and converts them to PropStore events. + * Using this as an intermediary, the external listeners do not need to set / manage external + * ZooKeeper watchers, they can register for PropStore events if they need to take active action on + * change detection. + * <p> + * Users of the PropStore.get() will get properties that match what is stored in ZooKeeper for each + * call and do not need to manage any caching. However, the ability to receive active notification + * without needed to register / manage ZooKeeper watchers external to the PropStore is provided in + * case other code is relying on active notifications. + * <p> + * The notification occurs on a separate thread from the ZooKeeper notification handling, but + * listeners should not perform lengthy operations on the notification thread so that other listener + * notifications are not delayed. + */ +public class PropStoreWatcher implements Watcher { + + private static final Logger log = LoggerFactory.getLogger(PropStoreWatcher.class); + + private final ExecutorService executorService = + ThreadPools.getServerThreadPools().createFixedThreadPool(1, "zoo_change_update", false); Review comment: Should this be static? ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/PropCacheId.java ########## @@ -0,0 +1,278 @@ +/* + * 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.accumulo.server.conf.store; + +import static org.apache.accumulo.core.Constants.ZCONFIG; +import static org.apache.accumulo.core.Constants.ZNAMESPACES; +import static org.apache.accumulo.core.Constants.ZNAMESPACE_CONF; +import static org.apache.accumulo.core.Constants.ZTABLES; +import static org.apache.accumulo.core.Constants.ZTABLE_CONF; + +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; + +import org.apache.accumulo.core.data.InstanceId; +import org.apache.accumulo.core.data.NamespaceId; +import org.apache.accumulo.core.data.TableId; +import org.apache.accumulo.fate.zookeeper.ZooUtil; +import org.apache.accumulo.server.ServerContext; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Provides a strongly-typed id for storing properties in ZooKeeper. The path in ZooKeeper is + * determined by the instance id and the type (system, namespace and table), with different root + * paths. + * <p> + * Provides utility methods from constructing different id based on type and methods to parse a + * ZooKeeper path and return a prop cache id. + */ +public class PropCacheId implements Comparable<PropCacheId> { + + public static final String PROP_NODE_NAME = "encoded_props"; + + // indices for path.split(); + public static final int TYPE_TOKEN_POSITION = 3; + public static final int IID_TOKEN_POSITION = 2; + public static final int ID_TOKEN_POSITION = 4; + + // remove starting slash from constant. + public static final String TABLES_NODE_NAME = ZTABLES.substring(1); + public static final String NAMESPACE_NODE_NAME = ZNAMESPACES.substring(1); + + private final String path; + private final IdType idType; + private final NamespaceId namespaceId; + private final TableId tableId; + + private PropCacheId(final String path, final IdType idType, final NamespaceId namespaceId, + final TableId tableId) { + this.path = path; + this.idType = idType; + this.namespaceId = namespaceId; + this.tableId = tableId; + } + + /** + * Instantiate a system prop cache id using the instance id from the context. + * + * @param context + * the system context specifying the instance id + * @return a prop cache id for system properties, + */ + public static PropCacheId forSystem(final ServerContext context) { + return forSystem(context.getInstanceID()); + } + + /** + * Instantiate a system prop cache id. + * + * @param instanceId + * the instance id. + * @return a prop cache id for system properties, + */ + public static PropCacheId forSystem(final InstanceId instanceId) { + return new PropCacheId(ZooUtil.getRoot(instanceId) + ZCONFIG + "/" + PROP_NODE_NAME, + IdType.SYSTEM, null, null); + } + + /** + * Instantiate a namespace prop cache id using the instance id from the context. + * + * @param context + * the system context specifying the instance id + * @param namespaceId + * the namespace id + * @return a prop cache id a namespaces properties, + */ + public static PropCacheId forNamespace(final ServerContext context, + final NamespaceId namespaceId) { + return forNamespace(context.getInstanceID(), namespaceId); + } + + /** + * Instantiate a namespace prop cache id using the instance id from the context. + * + * @param instanceId + * the instance id + * @param namespaceId + * the namespace id + * @return a prop cache id a namespaces properties, + */ + public static PropCacheId forNamespace(final InstanceId instanceId, + final NamespaceId namespaceId) { + return new PropCacheId(ZooUtil.getRoot(instanceId) + ZNAMESPACES + "/" + namespaceId.canonical() + + ZNAMESPACE_CONF + "/" + PROP_NODE_NAME, IdType.NAMESPACE, namespaceId, null); + } + + /** + * Instantiate a namespace prop cache id using the instance id from the context. + * + * @param context + * the system context specifying the instance id + * @param tableId + * the table id + * @return a prop cache id a namespaces properties, + */ + public static PropCacheId forTable(final ServerContext context, final TableId tableId) { + return forTable(context.getInstanceID(), tableId); + } + + /** + * Instantiate a namespace prop cache id using the instance id from the context. + * + * @param instanceId + * the instance id + * @param tableId + * the table id + * @return a prop cache id a namespaces properties, + */ + public static PropCacheId forTable(final InstanceId instanceId, final TableId tableId) { + return new PropCacheId(ZooUtil.getRoot(instanceId) + ZTABLES + "/" + tableId.canonical() + + ZTABLE_CONF + "/" + PROP_NODE_NAME, IdType.TABLE, null, tableId); + } + + /** + * Determine the prop cache id from a ZooKeeper path + * + * @param path + * the path + * @return the prop cache id + */ + public static Optional<PropCacheId> fromPath(final String path) { + String[] tokens = path.split("/"); + Review comment: Do you need to check the length of tokens or are you handling ArrayIndexOutOfBounds exceptions from where this is called? ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/impl/PropStoreMetrics.java ########## @@ -0,0 +1,92 @@ +/* + * 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.accumulo.server.conf.store.impl; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import org.apache.accumulo.core.metrics.MetricsProducer; +import org.apache.accumulo.core.metrics.MetricsUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; + +public class PropStoreMetrics implements MetricsProducer { + + private static final Logger log = LoggerFactory.getLogger(PropStoreMetrics.class); + + private Timer load; + private Counter refresh; + private Counter refreshLoad; + private Counter eviction; + private Counter zkError; + + @Override + public void registerMetrics(MeterRegistry registry) { + + load = Timer.builder(METRICS_PROPSTORE_LOAD_TIMER).description("prop store load time") + .tags(MetricsUtil.getCommonTags()).register(registry); + + refresh = + Counter.builder(METRICS_PROPSTORE_REFRESH_COUNT).description("prop store refresh count") + .tags(MetricsUtil.getCommonTags()).register(registry); + + refreshLoad = Counter.builder(METRICS_PROPSTORE_REFRESH_LOAD_COUNT) + .description("prop store refresh load count").tags(MetricsUtil.getCommonTags()) + .register(registry); + + eviction = + Counter.builder(METRICS_PROPSTORE_EVICTION_COUNT).description("prop store eviction count") + .tags(MetricsUtil.getCommonTags()).register(registry); + + zkError = Counter.builder(METRICS_PROPSTORE_ZK_ERROR_COUNT) + .description("prop store ZooKeeper error count").tags(MetricsUtil.getCommonTags()) + .register(registry); + + } + + public PropStoreMetrics() { + log.info("Creating PropStore metrics"); + } + + public void addLoadTime(final long value) { + log.info("Load time: {}", value); Review comment: set to debug? ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/PropCacheId.java ########## @@ -0,0 +1,278 @@ +/* + * 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.accumulo.server.conf.store; + +import static org.apache.accumulo.core.Constants.ZCONFIG; +import static org.apache.accumulo.core.Constants.ZNAMESPACES; +import static org.apache.accumulo.core.Constants.ZNAMESPACE_CONF; +import static org.apache.accumulo.core.Constants.ZTABLES; +import static org.apache.accumulo.core.Constants.ZTABLE_CONF; + +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; + +import org.apache.accumulo.core.data.InstanceId; +import org.apache.accumulo.core.data.NamespaceId; +import org.apache.accumulo.core.data.TableId; +import org.apache.accumulo.fate.zookeeper.ZooUtil; +import org.apache.accumulo.server.ServerContext; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Provides a strongly-typed id for storing properties in ZooKeeper. The path in ZooKeeper is + * determined by the instance id and the type (system, namespace and table), with different root + * paths. + * <p> + * Provides utility methods from constructing different id based on type and methods to parse a + * ZooKeeper path and return a prop cache id. + */ +public class PropCacheId implements Comparable<PropCacheId> { + + public static final String PROP_NODE_NAME = "encoded_props"; + + // indices for path.split(); + public static final int TYPE_TOKEN_POSITION = 3; + public static final int IID_TOKEN_POSITION = 2; + public static final int ID_TOKEN_POSITION = 4; + + // remove starting slash from constant. + public static final String TABLES_NODE_NAME = ZTABLES.substring(1); + public static final String NAMESPACE_NODE_NAME = ZNAMESPACES.substring(1); + + private final String path; + private final IdType idType; + private final NamespaceId namespaceId; + private final TableId tableId; + + private PropCacheId(final String path, final IdType idType, final NamespaceId namespaceId, + final TableId tableId) { + this.path = path; + this.idType = idType; + this.namespaceId = namespaceId; + this.tableId = tableId; + } + + /** + * Instantiate a system prop cache id using the instance id from the context. + * + * @param context + * the system context specifying the instance id + * @return a prop cache id for system properties, + */ + public static PropCacheId forSystem(final ServerContext context) { + return forSystem(context.getInstanceID()); + } + + /** + * Instantiate a system prop cache id. + * + * @param instanceId + * the instance id. + * @return a prop cache id for system properties, + */ + public static PropCacheId forSystem(final InstanceId instanceId) { + return new PropCacheId(ZooUtil.getRoot(instanceId) + ZCONFIG + "/" + PROP_NODE_NAME, + IdType.SYSTEM, null, null); + } + + /** + * Instantiate a namespace prop cache id using the instance id from the context. + * + * @param context + * the system context specifying the instance id + * @param namespaceId + * the namespace id + * @return a prop cache id a namespaces properties, + */ + public static PropCacheId forNamespace(final ServerContext context, + final NamespaceId namespaceId) { + return forNamespace(context.getInstanceID(), namespaceId); + } + + /** + * Instantiate a namespace prop cache id using the instance id from the context. + * + * @param instanceId + * the instance id + * @param namespaceId + * the namespace id + * @return a prop cache id a namespaces properties, + */ + public static PropCacheId forNamespace(final InstanceId instanceId, + final NamespaceId namespaceId) { + return new PropCacheId(ZooUtil.getRoot(instanceId) + ZNAMESPACES + "/" + namespaceId.canonical() + + ZNAMESPACE_CONF + "/" + PROP_NODE_NAME, IdType.NAMESPACE, namespaceId, null); + } + + /** + * Instantiate a namespace prop cache id using the instance id from the context. + * + * @param context + * the system context specifying the instance id + * @param tableId + * the table id + * @return a prop cache id a namespaces properties, + */ + public static PropCacheId forTable(final ServerContext context, final TableId tableId) { + return forTable(context.getInstanceID(), tableId); + } + + /** + * Instantiate a namespace prop cache id using the instance id from the context. + * + * @param instanceId + * the instance id + * @param tableId + * the table id + * @return a prop cache id a namespaces properties, + */ + public static PropCacheId forTable(final InstanceId instanceId, final TableId tableId) { + return new PropCacheId(ZooUtil.getRoot(instanceId) + ZTABLES + "/" + tableId.canonical() + + ZTABLE_CONF + "/" + PROP_NODE_NAME, IdType.TABLE, null, tableId); + } + + /** + * Determine the prop cache id from a ZooKeeper path + * + * @param path + * the path + * @return the prop cache id + */ + public static Optional<PropCacheId> fromPath(final String path) { + String[] tokens = path.split("/"); + + InstanceId instanceId = InstanceId.of(tokens[IID_TOKEN_POSITION]); + + IdType type = extractType(tokens); + + switch (type) { + case SYSTEM: + return Optional.of(PropCacheId.forSystem(instanceId)); + case NAMESPACE: + return Optional + .of(PropCacheId.forNamespace(instanceId, NamespaceId.of(tokens[ID_TOKEN_POSITION]))); + case TABLE: + return Optional.of(PropCacheId.forTable(instanceId, TableId.of(tokens[ID_TOKEN_POSITION]))); + case UNKNOWN: + default: + return Optional.empty(); + } + } + + /** + * Determine if the IdType is system, namespace or table from a tokenized path. To be a valid id, + * the final token is PROP_NODE_NAME and then the type is defined if the path has table or + * namespace in the path, otherwise it is assumed to be system. + * + * @param tokens + * a path split into String[] of tokens + * @return the id type. + */ + public static IdType extractType(final String[] tokens) { + if (tokens.length == 0 || !tokens[tokens.length - 1].equals(PROP_NODE_NAME)) { + // without tokens or it does not end with PROP_NAME_NAME + return IdType.UNKNOWN; + } + if (tokens[TYPE_TOKEN_POSITION].equals(TABLES_NODE_NAME)) { + return IdType.TABLE; + } + if (tokens[TYPE_TOKEN_POSITION].equals(NAMESPACE_NODE_NAME)) { + return IdType.NAMESPACE; + } + return IdType.SYSTEM; + } + + public String getPath() { + return path; + } + + public IdType getIdType() { + return idType; + } + + @Override + public int compareTo(@NonNull PropCacheId other) { + return Comparator.comparing(PropCacheId::getIdType).thenComparing(PropCacheId::getPath) + .compare(this, other); + } + + // TODO - remove optional and return null. + /** + * If the prop cache is for a namespace, return the namespace id. + * + * @return the namespace id. + */ + public Optional<NamespaceId> getNamespaceId() { + return Optional.ofNullable(namespaceId); + } + + /** + * if the prop cache is for a table, return the table id. + * + * @return the table id. + */ + public Optional<TableId> getTableId() { + return Optional.ofNullable(tableId); + } + + @Override + public boolean equals(Object o) { Review comment: equals() and compareTo() are not checking the same things. Is this by design? equals() could return true if the path is the same, but compareTo() could return not 0 if the id types are different. ########## File path: server/base/src/main/java/org/apache/accumulo/server/conf/store/impl/PropStoreMetrics.java ########## @@ -0,0 +1,92 @@ +/* + * 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.accumulo.server.conf.store.impl; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import org.apache.accumulo.core.metrics.MetricsProducer; +import org.apache.accumulo.core.metrics.MetricsUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; + +public class PropStoreMetrics implements MetricsProducer { + + private static final Logger log = LoggerFactory.getLogger(PropStoreMetrics.class); + + private Timer load; + private Counter refresh; + private Counter refreshLoad; + private Counter eviction; + private Counter zkError; + + @Override + public void registerMetrics(MeterRegistry registry) { + + load = Timer.builder(METRICS_PROPSTORE_LOAD_TIMER).description("prop store load time") + .tags(MetricsUtil.getCommonTags()).register(registry); + + refresh = + Counter.builder(METRICS_PROPSTORE_REFRESH_COUNT).description("prop store refresh count") + .tags(MetricsUtil.getCommonTags()).register(registry); + + refreshLoad = Counter.builder(METRICS_PROPSTORE_REFRESH_LOAD_COUNT) + .description("prop store refresh load count").tags(MetricsUtil.getCommonTags()) + .register(registry); + + eviction = + Counter.builder(METRICS_PROPSTORE_EVICTION_COUNT).description("prop store eviction count") + .tags(MetricsUtil.getCommonTags()).register(registry); + + zkError = Counter.builder(METRICS_PROPSTORE_ZK_ERROR_COUNT) + .description("prop store ZooKeeper error count").tags(MetricsUtil.getCommonTags()) + .register(registry); + + } + + public PropStoreMetrics() { + log.info("Creating PropStore metrics"); + } + + public void addLoadTime(final long value) { + log.info("Load time: {}", value); + load.record(Duration.ofMillis(value)); + log.info("Load count: {} time:{}", load.count(), load.totalTime(TimeUnit.MILLISECONDS)); Review comment: set to debug? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
