Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 47 additions & 13 deletions internal-api/src/main/java/datadog/trace/api/TagMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -1225,11 +1225,11 @@ static final class EmptyHolder {

/**
* Dense known-tag store (dense-tagmap-design §5). Values for KNOWN tags (those {@link
* KnownTagCodec#keyOf} resolves to a stored id) live in these INSERTION-ORDERED parallel arrays with
* NO per-tag {@link Entry} object — the allocation win. Lazily allocated on the first known-tag
* write ({@code null} until then, so all-unknown maps pay nothing) and grown x2 from {@link
* #KNOWN_INIT_CAP}. Matched by globalSerial via a linear scan ({@link #knownIndexOf}); reads
* aren't hot, so O(knownCount) is fine and positional indexing is deferred. Dormant until a
* KnownTagCodec#keyOf} resolves to a stored id) live in these INSERTION-ORDERED parallel arrays
* with NO per-tag {@link Entry} object — the allocation win. Lazily allocated on the first
* known-tag write ({@code null} until then, so all-unknown maps pay nothing) and grown x2 from
* {@link #KNOWN_INIT_CAP}. Matched by globalSerial via a linear scan ({@link #knownIndexOf});
* reads aren't hot, so O(knownCount) is fine and positional indexing is deferred. Dormant until a
* resolver is registered: {@code keyOf} returns 0, so nothing routes here and production is
* byte-identical.
*
Expand All @@ -1247,6 +1247,15 @@ static final class EmptyHolder {
private Object[] knownValues;
private int knownCount;

/**
* Bloom-style presence filter over the dense store: a set bit means {@code tagId} MAY be present
* (scan to confirm), a clear bit means it is DEFINITELY absent (skip the scan, append in O(1) —
* the common per-build case). A superset of the present ids' bits: set on every add, never
* cleared on remove (a stale bit only costs a scan, never a wrong answer). So correctness never
* depends on the position→bit collision rate; only the fast-path hit rate does.
*/
private long knownBloom;

private static final int KNOWN_INIT_CAP = 8;

/**
Expand Down Expand Up @@ -1552,27 +1561,45 @@ private void ensureKnownCapacity() {
}
}

/**
* Presence-filter bit for {@code tagId}. Crude position→bit map ({@code fieldPos} mod 64) to
* start; a collision-minimizing per-type coloring later only raises the hit rate — correctness
* never depends on it, because the scan is authoritative.
*/
private static long knownBloomBit(long tagId) {
return 1L << (KnownTagCodec.fieldPos(tagId) & 63);
}

/**
* Stores a known tag's value densely (no {@link Entry} alloc). Overwrites in place when present
* (returning the prior value materialized as an Entry, per the {@code Map} contract — usually
* discarded by {@code set}); otherwise appends, growing x2 as needed.
* discarded by {@code set}); otherwise appends, growing x2 as needed. The bloom filter skips the
* {@link #knownIndexOf} scan when the tag is definitely absent (the common per-build case), so an
* append is O(1) instead of O(n).
*/
private Entry putKnownValue(long tagId, Object value) {
int i = this.knownIndexOf(tagId);
if (i >= 0) {
Object prior = this.knownValues[i];
this.knownValues[i] = value;
return materializeKnown(tagId, prior);
long bit = knownBloomBit(tagId);
if ((this.knownBloom & bit) != 0) {
// maybe present -> scan to overwrite in place
int i = this.knownIndexOf(tagId);
if (i >= 0) {
Object prior = this.knownValues[i];
this.knownValues[i] = value;
return materializeKnown(tagId, prior);
}
// bloom false positive (collision) -> fall through to append
}
this.ensureKnownCapacity();
int slot = this.knownCount++;
this.knownIds[slot] = tagId;
this.knownValues[slot] = value;
this.knownBloom |= bit;
return null;
}

/** Raw dense value for {@code tagId}, or {@code null} when absent (no Entry, no boxing). */
private Object knownRawValue(long tagId) {
if ((this.knownBloom & knownBloomBit(tagId)) == 0) return null; // definitely absent, no scan
int i = this.knownIndexOf(tagId);
return i < 0 ? null : this.knownValues[i];
}
Expand All @@ -1581,6 +1608,7 @@ private Object knownRawValue(long tagId) {
* Removes a known tag from the dense store (swap-with-last), returning the prior Entry or null.
*/
private Entry removeKnown(long tagId) {
if ((this.knownBloom & knownBloomBit(tagId)) == 0) return null; // definitely absent
int i = this.knownIndexOf(tagId);
if (i < 0) return null;
Object prior = this.knownValues[i];
Expand All @@ -1589,6 +1617,8 @@ private Entry removeKnown(long tagId) {
this.knownValues[i] = this.knownValues[last];
this.knownIds[last] = 0L;
this.knownValues[last] = null;
// knownBloom intentionally NOT cleared: a stale-set bit only costs a scan; clearing could drop
// a bit still shared (via collision) by a present id -> false negative.
return materializeKnown(tagId, prior);
}

Expand All @@ -1603,7 +1633,8 @@ private static Entry materializeKnown(long tagId, Object value) {
* local bucket entry — known tags never bucket — so no bucket check is needed here.)
*/
private boolean parentDenseHidden(long tagId) {
if (this.knownIndexOf(tagId) >= 0) return true; // shadowed by a local dense entry
// shadowed by a local dense entry (bloom prunes the scan when definitely absent)
if ((this.knownBloom & knownBloomBit(tagId)) != 0 && this.knownIndexOf(tagId) >= 0) return true;
return this.removedFromParent != null
&& this.removedFromParent.contains(KnownTagCodec.nameOf(tagId)); // tombstoned
}
Expand Down Expand Up @@ -2006,6 +2037,7 @@ private void putAllIntoEmptyMap(OptimizedTagMap that) {
this.knownIds = Arrays.copyOf(that.knownIds, that.knownIds.length);
this.knownValues = Arrays.copyOf(that.knownValues, that.knownValues.length);
this.knownCount = that.knownCount;
this.knownBloom = that.knownBloom;
}
}

Expand Down Expand Up @@ -2048,7 +2080,8 @@ public void fillStringMap(Map<? super String, ? super String> stringMap) {
}
for (int i = 0; i < this.knownCount; ++i) {
stringMap.put(
KnownTagCodec.nameOf(this.knownIds[i]), TagValueConversions.toString(this.knownValues[i]));
KnownTagCodec.nameOf(this.knownIds[i]),
TagValueConversions.toString(this.knownValues[i]));
}
}

Expand Down Expand Up @@ -2393,6 +2426,7 @@ public void clear() {
this.knownIds = null;
this.knownValues = null;
this.knownCount = 0;
this.knownBloom = 0L;
}

public OptimizedTagMap freeze() {
Expand Down
Loading