Skip to content

Commit c9eceab

Browse files
committed
Add Maybe-returning tryGetOrCreateAsMaybe to Hashtable and FlatHashtable
Additive siblings to tryGetOrCreate on Hashtable.D1/D2 and FlatHashtable.D1/D2, wrapping the existing @Nullable-returning method in a Maybe rather than changing its signature. Each delegates to the existing tryGetOrCreate as its sole Maybe#of call site, keeping the allocation-free shape Maybe's class javadoc requires. Validates Maybe against a real caller: the client-side-stats PR (#12312) stacked on top of this one converts CardinalityLimitReporter to tryGetOrCreateAsMaybe(...).update(...).
1 parent c520bb0 commit c9eceab

6 files changed

Lines changed: 149 additions & 0 deletions

File tree

‎internal-api/src/main/java/datadog/trace/util/FlatHashtable.java‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,17 @@ public TEntry tryGetOrCreate(@Nullable K key, @Nonnull CreateStrategy<TEntry, K>
285285
return created;
286286
}
287287

288+
/**
289+
* {@link Maybe}-wrapped form of {@link #tryGetOrCreate}, delegating to it as the sole {@link
290+
* Maybe#of} call site -- see {@link Maybe}'s class javadoc for why that shape is required to
291+
* stay allocation-free. A growable table's {@link Maybe} is always present.
292+
*/
293+
@Nonnull
294+
public Maybe<TEntry> tryGetOrCreateAsMaybe(
295+
@Nullable K key, @Nonnull CreateStrategy<TEntry, K> createStrat) {
296+
return Maybe.of(tryGetOrCreate(key, createStrat));
297+
}
298+
288299
/**
289300
* Unconditionally adds {@code entry} ({@code true}), or {@code false} if a fixed table is full.
290301
* Comparison-free and caller-responsible (same contract as {@link FlatHashtable#insert}): the
@@ -476,6 +487,18 @@ public TEntry tryGetOrCreate(
476487
return created;
477488
}
478489

490+
/**
491+
* Two-key analogue of {@link D1#tryGetOrCreateAsMaybe}: {@link Maybe}-wrapped form of {@link
492+
* #tryGetOrCreate}, delegating to it as the sole {@link Maybe#of} call site.
493+
*/
494+
@Nonnull
495+
public Maybe<TEntry> tryGetOrCreateAsMaybe(
496+
@Nullable K1 key1,
497+
@Nullable K2 key2,
498+
@Nonnull CreateStrategy2<TEntry, K1, K2> createStrat) {
499+
return Maybe.of(tryGetOrCreate(key1, key2, createStrat));
500+
}
501+
479502
/**
480503
* Unconditionally adds {@code entry} ({@code true}), or {@code false} if a fixed table is full.
481504
* Comparison-free and caller-responsible: the caller must ensure {@code (key1, key2)} is

‎internal-api/src/main/java/datadog/trace/util/Hashtable.java‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,23 @@ public TEntry tryGetOrCreate(
339339
return newEntry;
340340
}
341341

342+
/**
343+
* {@link Maybe}-wrapped form of {@link #tryGetOrCreate}, for callers that want to guard the
344+
* refused-create case with {@link Maybe#update} rather than a manual null check:
345+
*
346+
* <pre>{@code
347+
* table.tryGetOrCreateAsMaybe(key, Counter::new).update(n, ADD);
348+
* }</pre>
349+
*
350+
* <p>Exactly one {@link Maybe#of} call site, fed by delegating to {@link #tryGetOrCreate} --
351+
* see {@link Maybe}'s class javadoc for why that shape is required to stay allocation-free.
352+
*/
353+
@Nonnull
354+
public Maybe<TEntry> tryGetOrCreateAsMaybe(
355+
@Nullable K key, @Nonnull Function<? super K, ? extends TEntry> creator) {
356+
return Maybe.of(tryGetOrCreate(key, creator));
357+
}
358+
342359
/**
343360
* {@link #tryGetOrCreate} followed by {@code updater}, returning whether the update happened.
344361
*
@@ -653,6 +670,18 @@ public TEntry tryGetOrCreate(
653670
return newEntry;
654671
}
655672

673+
/**
674+
* Two-key analogue of {@link D1#tryGetOrCreateAsMaybe}: {@link Maybe}-wrapped form of {@link
675+
* #tryGetOrCreate}, delegating to it as the sole {@link Maybe#of} call site.
676+
*/
677+
@Nonnull
678+
public Maybe<TEntry> tryGetOrCreateAsMaybe(
679+
@Nullable K1 key1,
680+
@Nullable K2 key2,
681+
@Nonnull BiFunction<? super K1, ? super K2, ? extends TEntry> creator) {
682+
return Maybe.of(tryGetOrCreate(key1, key2, creator));
683+
}
684+
656685
/**
657686
* Two-key analogue of {@link D1#tryGetOrUpdate(Object, Function, Consumer)}: applies {@code
658687
* updater} to the entry for {@code (key1, key2)}, creating one if absent, and returns whether

‎internal-api/src/test/java/datadog/trace/util/FlatHashtableD1Test.java‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,27 @@ void fixedGetOrCreateCapsWhenFull() {
257257
assertSame(a, table.tryGetOrCreate("a", k -> new StringIntEntry(k, 99)));
258258
}
259259

260+
@Test
261+
void fixedGetOrCreateAsMaybeIsAbsentWhenFullButStillReturnsHits() {
262+
FlatHashtable.D1<String, StringIntEntry> table = fixed(2);
263+
table.tryGetOrCreate("a", k -> new StringIntEntry(k, 1));
264+
table.tryGetOrCreate("b", k -> new StringIntEntry(k, 2));
265+
266+
assertFalse(table.tryGetOrCreateAsMaybe("c", k -> new StringIntEntry(k, 3)).isPresent());
267+
268+
Maybe<StringIntEntry> hit = table.tryGetOrCreateAsMaybe("a", k -> new StringIntEntry(k, 99));
269+
assertEquals(1, hit.getOrNull().value, "existing entry is still returned even at capacity");
270+
}
271+
272+
@Test
273+
void growableGetOrCreateAsMaybeIsAlwaysPresent() {
274+
FlatHashtable.D1<String, StringIntEntry> table = growable(1);
275+
for (int i = 0; i < 50; i++) {
276+
assertTrue(table.tryGetOrCreateAsMaybe("k" + i, k -> new StringIntEntry(k, 0)).isPresent());
277+
}
278+
assertEquals(50, table.size());
279+
}
280+
260281
@Test
261282
void fixedInsertReturnsFalseWhenFull() {
262283
FlatHashtable.D1<String, StringIntEntry> table = fixed(2);

‎internal-api/src/test/java/datadog/trace/util/FlatHashtableD2Test.java‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,31 @@ void fixedGetOrCreateCapsWhenFull() {
226226
assertSame(a, table.tryGetOrCreate("a", 1, (k1, k2) -> new PairEntry(k1, k2, 99)));
227227
}
228228

229+
@Test
230+
void fixedGetOrCreateAsMaybeIsAbsentWhenFullButStillReturnsHits() {
231+
FlatHashtable.D2<String, Integer, PairEntry> table = fixed(2);
232+
table.tryGetOrCreate("a", 1, (k1, k2) -> new PairEntry(k1, k2, 1));
233+
table.tryGetOrCreate("b", 2, (k1, k2) -> new PairEntry(k1, k2, 2));
234+
235+
assertFalse(
236+
table.tryGetOrCreateAsMaybe("c", 3, (k1, k2) -> new PairEntry(k1, k2, 3)).isPresent());
237+
238+
Maybe<PairEntry> hit =
239+
table.tryGetOrCreateAsMaybe("a", 1, (k1, k2) -> new PairEntry(k1, k2, 99));
240+
assertEquals(1, hit.getOrNull().value, "existing entry is still returned even at capacity");
241+
}
242+
243+
@Test
244+
void growableGetOrCreateAsMaybeIsAlwaysPresent() {
245+
FlatHashtable.D2<String, Integer, PairEntry> table = growable(1);
246+
for (int i = 0; i < 50; i++) {
247+
Maybe<PairEntry> maybe =
248+
table.tryGetOrCreateAsMaybe("k", i, (k1, k2) -> new PairEntry(k1, k2, k2));
249+
assertTrue(maybe.isPresent());
250+
}
251+
assertEquals(50, table.size());
252+
}
253+
229254
@Test
230255
void fixedInsertReturnsFalseWhenFull() {
231256
FlatHashtable.D2<String, Integer, PairEntry> table = fixed(2);

‎internal-api/src/test/java/datadog/trace/util/HashtableD1Test.java‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,42 @@ void getOrCreateNullKeyIsPermitted() {
245245
assertEquals(1, table.size());
246246
}
247247

248+
@Test
249+
void getOrCreateAsMaybeOnMissBuildsEntryViaCreator() {
250+
Hashtable.D1<String, StringIntEntry> table = Hashtable.D1.createCapped(StringIntEntry.class, 8);
251+
Maybe<StringIntEntry> maybe =
252+
table.tryGetOrCreateAsMaybe("foo", k -> new StringIntEntry(k, 42));
253+
assertTrue(maybe.isPresent());
254+
assertEquals(42, maybe.getOrNull().value);
255+
assertSame(table.get("foo"), maybe.getOrNull());
256+
}
257+
258+
@Test
259+
void getOrCreateAsMaybeReturnsAbsentOnceAtCapacityButStillReturnsHits() {
260+
Hashtable.D1<String, StringIntEntry> table = Hashtable.D1.createCapped(StringIntEntry.class, 2);
261+
table.insert(new StringIntEntry("a", 1));
262+
table.insert(new StringIntEntry("b", 2));
263+
264+
assertFalse(table.tryGetOrCreateAsMaybe("c", k -> new StringIntEntry(k, 3)).isPresent());
265+
assertEquals(2, table.size());
266+
267+
Maybe<StringIntEntry> hit = table.tryGetOrCreateAsMaybe("a", k -> new StringIntEntry(k, 999));
268+
assertEquals(1, hit.getOrNull().value, "existing entry is still returned even at capacity");
269+
}
270+
271+
@Test
272+
void getOrCreateAsMaybeUpdateAppliesOnlyWhenPresent() {
273+
Hashtable.D1<String, StringIntEntry> table = Hashtable.D1.createCapped(StringIntEntry.class, 1);
274+
table.insert(new StringIntEntry("a", 1));
275+
276+
ObjLongConsumer<StringIntEntry> add = (e, n) -> e.value += n;
277+
table.tryGetOrCreateAsMaybe("a", k -> new StringIntEntry(k, 0)).update(5L, add);
278+
assertEquals(6, table.get("a").value);
279+
280+
table.tryGetOrCreateAsMaybe("b", k -> new StringIntEntry(k, 0)).update(5L, add);
281+
assertNull(table.get("b"), "refused create at capacity leaves nothing to update");
282+
}
283+
248284
@Test
249285
void insertReturnsFalseOnceAtCapacity() {
250286
Hashtable.D1<String, StringIntEntry> table = Hashtable.D1.createCapped(StringIntEntry.class, 2);

‎internal-api/src/test/java/datadog/trace/util/HashtableD2Test.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,21 @@ void getOrCreateReturnsNullOnceAtCapacityButStillReturnsHits() {
206206
assertEquals(100, hit.value, "existing entry is still returned even at capacity");
207207
}
208208

209+
@Test
210+
void getOrCreateAsMaybeReturnsAbsentOnceAtCapacityButStillReturnsHits() {
211+
Hashtable.D2<String, Integer, PairEntry> table = Hashtable.D2.createCapped(PairEntry.class, 2);
212+
table.insert(new PairEntry("a", 1, 100));
213+
table.insert(new PairEntry("b", 2, 200));
214+
215+
assertFalse(
216+
table.tryGetOrCreateAsMaybe("c", 3, (k1, k2) -> new PairEntry(k1, k2, 300)).isPresent());
217+
assertEquals(2, table.size());
218+
219+
Maybe<PairEntry> hit =
220+
table.tryGetOrCreateAsMaybe("a", 1, (k1, k2) -> new PairEntry(k1, k2, 999));
221+
assertEquals(100, hit.getOrNull().value, "existing entry is still returned even at capacity");
222+
}
223+
209224
@Test
210225
void tryInsertOrReplaceStillReplacesAtCapacityButRefusesFreshInsert() {
211226
Hashtable.D2<String, Integer, PairEntry> table = Hashtable.D2.createCapped(PairEntry.class, 2);

0 commit comments

Comments
 (0)