-
Notifications
You must be signed in to change notification settings - Fork 900
Memory Mode support: Adding memory mode, and implementing it for Asynchronous Instruments #5709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jack-berg
merged 45 commits into
open-telemetry:main
from
streamnative:memory-allocations-async
Sep 26, 2023
Merged
Changes from all commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
cb35698
With pool that is based on array
asafm cd5a0f7
Async now work at 99.8% less memory allocations
asafm 216a6f5
Working code, with beautified code (almost all) and less code in Pool…
asafm 8639239
Code looks good. Next are tests
asafm 7c7507d
Unit tests done
asafm 01d2215
jmh test almost ready with prototype
asafm e8ac068
test/code is ready
asafm bcc1a73
Merge remote-tracking branch 'origin/main' into memory-allocations-async
asafm d667889
Fixed all Gradle check task errors
asafm 8cce0c4
Small rename
asafm 501f819
Removed JOL. Added javadocs.
asafm 0ecb4ec
Removed draft code
asafm 93f25eb
Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/…
asafm 4aea7c9
Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/…
asafm 01513f3
Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/…
asafm 25ced28
Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/interna…
asafm 4a35ccd
Added internal warning
asafm 52278e6
Change naming of ImmutableMeasurement factory method to be consistent…
asafm d14f275
Making code more readable with 2 with* methods on Measurement
asafm 62a71bd
Making code more readable with replacing switch with if for memory mode
asafm 6648c7c
PR fixes, including perf improvement.
asafm 031f2c2
PR fixes
asafm 6093c38
More PR fixes
asafm e2a9007
Merging
asafm 098ca71
Merge remote-tracking branch 'origin/main' into memory-allocations-async
asafm a7a481e
spotless fixes
asafm f7c1608
Removed nullable from MutableDoublePointData
asafm ed02001
Merge remote-tracking branch 'origin/main' into memory-allocations-async
asafm 0ea62f1
Fix tiny imports issue
asafm 4c32a26
Merge remote-tracking branch 'origin/main' into memory-allocations-async
asafm 16d4713
checkstyle
asafm 69ecf76
More PR fixes
asafm 8e52652
More PR fixes
asafm cfbf50c
More PR fixes
asafm 59c713e
Trying to see if this fixes failure of AsynchronousMetricStorageGarba…
asafm ea04e14
Merge remote-tracking branch 'origin/main' into memory-allocations-async
asafm 0e3f1c1
Update sdk/common/src/main/java/io/opentelemetry/sdk/common/export/Me…
asafm 5b2f3ba
Update sdk/common/src/main/java/io/opentelemetry/sdk/common/export/Me…
asafm 87c9073
Update sdk/common/src/main/java/io/opentelemetry/sdk/common/export/Me…
asafm 4964d6a
Update sdk/common/src/main/java/io/opentelemetry/sdk/common/export/Me…
asafm b6f1ef6
Update sdk/common/src/main/java/io/opentelemetry/sdk/common/export/Me…
asafm 52582d8
Update sdk/common/src/main/java/io/opentelemetry/sdk/common/export/Me…
asafm 8cf00ba
style check fixes
asafm 0d09946
Add API changes and improved instructions a bit
asafm b5378c5
MD fixes
asafm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 10 additions & 1 deletion
11
docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,11 @@ | ||
Comparing source compatibility of against | ||
No changes. | ||
+++ NEW ENUM: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.common.export.MemoryMode (compatible) | ||
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. | ||
+++ NEW INTERFACE: java.lang.constant.Constable | ||
+++ NEW INTERFACE: java.lang.Comparable | ||
+++ NEW INTERFACE: java.io.Serializable | ||
+++ NEW SUPERCLASS: java.lang.Enum | ||
+++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.common.export.MemoryMode REUSABLE_DATA | ||
+++ NEW FIELD: PUBLIC(+) STATIC(+) FINAL(+) io.opentelemetry.sdk.common.export.MemoryMode IMMUTABLE_DATA | ||
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.export.MemoryMode valueOf(java.lang.String) | ||
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.export.MemoryMode[] values() |
10 changes: 9 additions & 1 deletion
10
docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,10 @@ | ||
Comparing source compatibility of against | ||
No changes. | ||
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.MetricExporter (not serializable) | ||
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0 | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.export.MemoryMode getMemoryMode() | ||
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.MetricReader (not serializable) | ||
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0 | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.export.MemoryMode getMemoryMode() | ||
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.metrics.export.PeriodicMetricReader (not serializable) | ||
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0 | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.export.MemoryMode getMemoryMode() |
12 changes: 11 additions & 1 deletion
12
docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,12 @@ | ||
Comparing source compatibility of against | ||
No changes. | ||
*** MODIFIED CLASS: PUBLIC io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader (not serializable) | ||
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0 | ||
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder builder() | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.export.MemoryMode getMemoryMode() | ||
+++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder (not serializable) | ||
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. | ||
+++ NEW SUPERCLASS: java.lang.Object | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader build() | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder setAggregationTemporalitySelector(io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector) | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder setDefaultAggregationSelector(io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector) | ||
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.exporter.InMemoryMetricReaderBuilder setMemoryMode(io.opentelemetry.sdk.common.export.MemoryMode) |
26 changes: 26 additions & 0 deletions
26
sdk/common/src/main/java/io/opentelemetry/sdk/common/export/MemoryMode.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.sdk.common.export; | ||
|
||
/** The memory semantics of the SDK. */ | ||
public enum MemoryMode { | ||
|
||
/** | ||
* Reuses objects to reduce allocations. | ||
* | ||
* <p>In this mode, the SDK reuses objects to reduce allocations, at the expense of disallowing | ||
* concurrent collections / exports. | ||
*/ | ||
REUSABLE_DATA, | ||
|
||
/** | ||
* Uses immutable data structures. | ||
* | ||
* <p>In this mode, the SDK passes immutable objects to exporters / readers, increasing | ||
* allocations but ensuring safe concurrent exports. | ||
*/ | ||
IMMUTABLE_DATA | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 128 additions & 0 deletions
128
...metry/sdk/metrics/internal/state/AsynchronousMetricStorageGarbageCollectionBenchmark.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.sdk.metrics.internal.state; | ||
|
||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.sdk.common.export.MemoryMode; | ||
import io.opentelemetry.sdk.metrics.Aggregation; | ||
import io.opentelemetry.sdk.metrics.SdkMeterProvider; | ||
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; | ||
import io.opentelemetry.sdk.metrics.data.AggregationTemporality; | ||
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; | ||
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil; | ||
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter; | ||
import java.time.Duration; | ||
import java.util.List; | ||
import java.util.Random; | ||
import java.util.concurrent.TimeUnit; | ||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.BenchmarkMode; | ||
import org.openjdk.jmh.annotations.Fork; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
import org.openjdk.jmh.annotations.Param; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import org.openjdk.jmh.annotations.Setup; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.TearDown; | ||
import org.openjdk.jmh.annotations.Threads; | ||
import org.openjdk.jmh.annotations.Warmup; | ||
|
||
/** | ||
* Run this through {@link AsynchronousMetricStorageGarbageCollectionBenchmarkTest}, as it runs it | ||
* embedded with the GC profiler which what this test designed for (No need for command line run) | ||
* | ||
* <p>This test creates 10 asynchronous counters (any asynchronous instrument will do as the code | ||
* path is almost the same for all async instrument types), and 1000 attribute sets. Each time the | ||
* test runs, it calls `flush` which effectively calls the callback for each counter. Each such | ||
* callback records a random number for each of the 1000 attribute sets. The result list ends up in | ||
* {@link NoopMetricExporter} which does nothing with it. | ||
* | ||
* <p>This is repeated 100 times, collectively called Operation in the statistics and each such | ||
* operation is repeated 20 times - known as Iteration. | ||
* | ||
* <p>Each such test is repeated, with a brand new JVM, for all combinations of {@link MemoryMode} | ||
* and {@link AggregationTemporality}. This is done since each combination has a different code | ||
* path. | ||
*/ | ||
@BenchmarkMode(Mode.SingleShotTime) | ||
@OutputTimeUnit(TimeUnit.NANOSECONDS) | ||
@Measurement(iterations = 20, batchSize = 100) | ||
@Warmup(iterations = 10, batchSize = 10) | ||
@Fork(1) | ||
public class AsynchronousMetricStorageGarbageCollectionBenchmark { | ||
|
||
@State(value = Scope.Benchmark) | ||
@SuppressWarnings("SystemOut") | ||
public static class ThreadState { | ||
private final int cardinality; | ||
private final int countersCount; | ||
@Param public AggregationTemporality aggregationTemporality; | ||
@Param public MemoryMode memoryMode; | ||
SdkMeterProvider sdkMeterProvider; | ||
private final Random random = new Random(); | ||
List<Attributes> attributesList; | ||
|
||
/** Creates a ThreadState. */ | ||
@SuppressWarnings("unused") | ||
public ThreadState() { | ||
cardinality = 1000; | ||
countersCount = 10; | ||
} | ||
|
||
@SuppressWarnings("SpellCheckingInspection") | ||
@Setup | ||
public void setup() { | ||
PeriodicMetricReader metricReader = | ||
PeriodicMetricReader.builder( | ||
// Configure an exporter that configures the temporality and aggregation | ||
// for the test case, but otherwise drops the data on export | ||
new NoopMetricExporter(aggregationTemporality, Aggregation.sum(), memoryMode)) | ||
// Effectively disable periodic reading so reading is only done on #flush() | ||
.setInterval(Duration.ofSeconds(Integer.MAX_VALUE)) | ||
.build(); | ||
SdkMeterProviderBuilder builder = SdkMeterProvider.builder(); | ||
SdkMeterProviderUtil.registerMetricReaderWithCardinalitySelector( | ||
builder, metricReader, unused -> cardinality + 1); | ||
|
||
attributesList = AttributesGenerator.generate(cardinality); | ||
|
||
// Disable examplars | ||
SdkMeterProviderUtil.setExemplarFilter(builder, ExemplarFilter.alwaysOff()); | ||
|
||
sdkMeterProvider = builder.build(); | ||
for (int i = 0; i < countersCount; i++) { | ||
sdkMeterProvider | ||
.get("meter") | ||
.counterBuilder("counter" + i) | ||
.buildWithCallback( | ||
observableLongMeasurement -> { | ||
for (int j = 0; j < attributesList.size(); j++) { | ||
Attributes attributes = attributesList.get(j); | ||
observableLongMeasurement.record(random.nextInt(10_000), attributes); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
@TearDown | ||
public void tearDown() { | ||
sdkMeterProvider.shutdown().join(10, TimeUnit.SECONDS); | ||
} | ||
} | ||
|
||
/** | ||
* Collects all asynchronous instruments metric data. | ||
* | ||
* @param threadState thread-state | ||
*/ | ||
@Benchmark | ||
@Threads(value = 1) | ||
public void recordAndCollect(ThreadState threadState) { | ||
threadState.sdkMeterProvider.forceFlush().join(10, TimeUnit.SECONDS); | ||
} | ||
} |
106 changes: 106 additions & 0 deletions
106
...y/sdk/metrics/internal/state/AsynchronousMetricStorageGarbageCollectionBenchmarkTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.sdk.metrics.internal.state; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo; | ||
import io.opentelemetry.sdk.common.export.MemoryMode; | ||
import io.opentelemetry.sdk.metrics.data.AggregationTemporality; | ||
import io.opentelemetry.sdk.resources.Resource; | ||
import java.util.Collection; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import org.assertj.core.data.Offset; | ||
import org.junit.jupiter.api.Assumptions; | ||
import org.junit.jupiter.api.Test; | ||
import org.openjdk.jmh.infra.BenchmarkParams; | ||
import org.openjdk.jmh.results.BenchmarkResult; | ||
import org.openjdk.jmh.results.Result; | ||
import org.openjdk.jmh.results.RunResult; | ||
import org.openjdk.jmh.runner.Runner; | ||
import org.openjdk.jmh.runner.RunnerException; | ||
import org.openjdk.jmh.runner.options.Options; | ||
import org.openjdk.jmh.runner.options.OptionsBuilder; | ||
|
||
public class AsynchronousMetricStorageGarbageCollectionBenchmarkTest { | ||
|
||
/** | ||
* This test validates that in {@link MemoryMode#REUSABLE_DATA}, {@link | ||
* AsynchronousMetricStorage#collect(Resource, InstrumentationScopeInfo, long, long)} barely | ||
* allocates memory which is then subsequently garbage collected. It is done so comparatively to | ||
* {@link MemoryMode#IMMUTABLE_DATA}, | ||
* | ||
* <p>It runs the JMH test {@link AsynchronousMetricStorageGarbageCollectionBenchmark} with GC | ||
* profiler, and measures for each parameter combination the garbage collector normalized rate | ||
* (bytes allocated per Operation). | ||
* | ||
* <p>Memory allocations can be hidden even at an innocent foreach loop on a collection, which | ||
* under the hood allocates an internal object O(N) times. Someone can accidentally refactor such | ||
* loop, resulting in 30% increase of garbage collected objects during a single collect() run. | ||
*/ | ||
@SuppressWarnings("rawtypes") | ||
@Test | ||
public void normalizedAllocationRateTest() throws RunnerException { | ||
// GitHub CI has an environment variable (CI=true). We can use it to skip | ||
// this test since it's a lengthy one (roughly 10 seconds) and have it running | ||
// only in GitHub CI | ||
Assumptions.assumeTrue( | ||
"true".equals(System.getenv("CI")), | ||
"This test should only run in GitHub CI since it's long"); | ||
|
||
// Runs AsynchronousMetricStorageMemoryProfilingBenchmark | ||
// with garbage collection profiler | ||
Options opt = | ||
new OptionsBuilder() | ||
.include(AsynchronousMetricStorageGarbageCollectionBenchmark.class.getSimpleName()) | ||
.addProfiler("gc") | ||
.shouldFailOnError(true) | ||
.jvmArgs("-Xmx1500m") | ||
.build(); | ||
Collection<RunResult> results = new Runner(opt).run(); | ||
|
||
// Collect the normalized GC allocation rate per parameters combination | ||
Map<String, Map<String, Double>> resultMap = new HashMap<>(); | ||
for (RunResult result : results) { | ||
for (BenchmarkResult benchmarkResult : result.getBenchmarkResults()) { | ||
BenchmarkParams benchmarkParams = benchmarkResult.getParams(); | ||
|
||
String memoryMode = benchmarkParams.getParam("memoryMode"); | ||
String aggregationTemporality = benchmarkParams.getParam("aggregationTemporality"); | ||
assertThat(memoryMode).isNotNull(); | ||
assertThat(aggregationTemporality).isNotNull(); | ||
|
||
Map<String, Result> secondaryResults = benchmarkResult.getSecondaryResults(); | ||
Result allocRateNorm = secondaryResults.get("gc.alloc.rate.norm"); | ||
assertThat(allocRateNorm) | ||
.describedAs("Allocation rate in secondary results: %s", secondaryResults) | ||
.isNotNull(); | ||
|
||
resultMap | ||
.computeIfAbsent(aggregationTemporality, k -> new HashMap<>()) | ||
.put(memoryMode, allocRateNorm.getScore()); | ||
} | ||
} | ||
|
||
assertThat(resultMap).hasSameSizeAs(AggregationTemporality.values()); | ||
|
||
// Asserts that reusable data GC allocation rate is a tiny fraction of immutable data | ||
// GC allocation rate | ||
resultMap.forEach( | ||
(aggregationTemporality, memoryModeToAllocRateMap) -> { | ||
Double immutableDataAllocRate = | ||
memoryModeToAllocRateMap.get(MemoryMode.IMMUTABLE_DATA.toString()); | ||
Double reusableDataAllocRate = | ||
memoryModeToAllocRateMap.get(MemoryMode.REUSABLE_DATA.toString()); | ||
|
||
assertThat(immutableDataAllocRate).isNotNull().isNotZero(); | ||
assertThat(reusableDataAllocRate).isNotNull().isNotZero(); | ||
assertThat(100 - (reusableDataAllocRate / immutableDataAllocRate) * 100) | ||
.isCloseTo(99.8, Offset.offset(2.0)); | ||
}); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
...rc/jmhBasedTest/java/io/opentelemetry/sdk/metrics/internal/state/AttributesGenerator.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.sdk.metrics.internal.state; | ||
|
||
import io.opentelemetry.api.common.Attributes; | ||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Random; | ||
|
||
public class AttributesGenerator { | ||
|
||
private AttributesGenerator() {} | ||
|
||
/** | ||
* Generates a list of unique attributes, with a single attribute key, and random value. | ||
* | ||
* @param uniqueAttributesCount The amount of unique attribute sets to generate | ||
* @return The list of generates {@link Attributes} | ||
*/ | ||
public static List<Attributes> generate(int uniqueAttributesCount) { | ||
Random random = new Random(); | ||
HashSet<String> attributeSet = new HashSet<>(); | ||
ArrayList<Attributes> attributesList = new ArrayList<>(uniqueAttributesCount); | ||
String last = "aaaaaaaaaaaaaaaaaaaaaaaaaa"; | ||
for (int i = 0; i < uniqueAttributesCount; i++) { | ||
char[] chars = last.toCharArray(); | ||
int attempts = 0; | ||
do { | ||
chars[random.nextInt(last.length())] = (char) (random.nextInt(26) + 'a'); | ||
} while (attributeSet.contains(new String(chars)) && ++attempts < 1000); | ||
if (attributeSet.contains(new String(chars))) { | ||
throw new IllegalStateException("Couldn't create new random attributes"); | ||
} | ||
last = new String(chars); | ||
attributesList.add(Attributes.builder().put("key", last).build()); | ||
attributeSet.add(last); | ||
} | ||
|
||
return attributesList; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.