nstream.reflect.model.NodeStats Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nstream-reflect Show documentation
Show all versions of nstream-reflect Show documentation
Web Agent introspection runtime
// Copyright 2015-2024 Nstream, inc.
//
// Licensed under the Redis Source Available License 2.0 (RSALv2) Agreement;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://redis.com/legal/rsalv2-agreement/
//
// 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 nstream.reflect.model;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import swim.collections.HashTrieMap;
import swim.structure.Form;
import swim.structure.Item;
import swim.structure.Kind;
import swim.structure.Record;
import swim.structure.Value;
import swim.uri.Uri;
@SuppressWarnings("checkstyle:VisibilityModifier")
public class NodeStats {
public final Value nodeKey;
public volatile long nodeCount;
public volatile HashTrieMap laneStats;
public NodeStats(Value nodeKey, long nodeCount, HashTrieMap laneStats) {
this.nodeKey = nodeKey;
this.nodeCount = nodeCount;
this.laneStats = laneStats;
}
public NodeStats(Value nodeKey, HashTrieMap laneStats) {
this(nodeKey, 0L, laneStats);
}
public NodeStats(Value nodeKey) {
this(nodeKey, 0L, HashTrieMap.empty());
}
public void didOpenNode() {
NODE_COUNT.incrementAndGet(this);
}
public void didCloseNode() {
NODE_COUNT.decrementAndGet(this);
}
public LaneStats getOrCreateLaneStats(Uri laneUri) {
LaneStats lane;
do {
final HashTrieMap oldLaneStats = this.laneStats;
lane = oldLaneStats.get(laneUri);
if (lane == null) {
lane = new LaneStats(laneUri);
final HashTrieMap newLaneStats = oldLaneStats.updated(laneUri, lane);
if (LANE_STATS.compareAndSet(this, oldLaneStats, newLaneStats)) {
break;
}
} else {
break;
}
} while (true);
return lane;
}
public void accumulate(NodeStats node) {
NODE_COUNT.addAndGet(this, node.nodeCount);
for (LaneStats lane : this.laneStats.values()) {
accumulate(lane);
}
}
public void accumulate(LaneStats stats) {
final LaneStats lane = getOrCreateLaneStats(stats.laneUri);
lane.accumulate(stats);
}
public void supersede(NodeStats total, NodeStats delta) {
final long newNodeCount = this.nodeCount;
final long oldNodeCount = NODE_COUNT.getAndSet(total, newNodeCount);
NODE_COUNT.addAndGet(delta, newNodeCount - oldNodeCount);
for (LaneStats lane : this.laneStats.values()) {
final LaneStats laneTotal = total.getOrCreateLaneStats(lane.laneUri);
final LaneStats laneDelta = delta.getOrCreateLaneStats(lane.laneUri);
lane.supersede(laneTotal, laneDelta);
}
}
public NodeStats getAndReset() {
final long nodeCount = NODE_COUNT.getAndSet(this, 0L);
final HashTrieMap oldLaneStats = this.laneStats;
HashTrieMap newLaneStats = HashTrieMap.empty();
for (LaneStats lane : oldLaneStats.values()) {
newLaneStats = newLaneStats.updated(lane.laneUri, lane.getAndReset());
}
return new NodeStats(nodeKey, nodeCount, newLaneStats);
}
public NodeStats get() {
final long nodeCount = this.nodeCount;
final HashTrieMap oldLaneStats = this.laneStats;
HashTrieMap newLaneStats = HashTrieMap.empty();
for (LaneStats lane : oldLaneStats.values()) {
newLaneStats = newLaneStats.updated(lane.laneUri, lane.get());
}
return new NodeStats(nodeKey, nodeCount, newLaneStats);
}
public Value toValue() {
return form().mold(this).toValue();
}
static final AtomicLongFieldUpdater NODE_COUNT =
AtomicLongFieldUpdater.newUpdater(NodeStats.class, "nodeCount");
@SuppressWarnings("unchecked")
static final AtomicReferenceFieldUpdater> LANE_STATS =
AtomicReferenceFieldUpdater.newUpdater(NodeStats.class, (Class>) (Class>) HashTrieMap.class, "laneStats");
private static Form form;
@Kind
public static Form form() {
if (form == null) {
form = new NodeStatsForm();
}
return form;
}
}
final class NodeStatsForm extends Form {
@Override
public String tag() {
return "node";
}
@Override
public Class> type() {
return NodeStats.class;
}
@Override
public Item mold(NodeStats stats) {
if (stats != null) {
final Record record = Record.create();
record.attr(tag(), stats.nodeKey);
if (stats.nodeCount > 0L) {
record.slot("nodeCount", stats.nodeCount);
}
for (LaneStats lane : stats.laneStats.values()) {
record.item(lane.toValue());
}
return record;
} else {
return Item.extant();
}
}
@Override
public NodeStats cast(Item item) {
final Value value = item.toValue();
final Value nodeKey = value.getAttr(tag());
if (nodeKey.isDefined()) {
long nodeCount = 0L;
HashTrieMap laneStats = HashTrieMap.empty();
for (Item member : value) {
if ("nodeCount".equals(member.key().stringValue())) {
nodeCount = member.toValue().longValue(0L);
continue;
}
final LaneStats lane = LaneStats.form().cast(member);
if (lane != null) {
laneStats = laneStats.updated(lane.laneUri, lane);
}
}
return new NodeStats(nodeKey, nodeCount, laneStats);
}
return null;
}
}