Skip to content
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

PCollection data sampling for Java SDK harness #25064 #25354

Merged
merged 43 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
cd630e4
Data Sampling Java Impl
Jan 26, 2023
e530d14
comments
Jan 26, 2023
a09f5d6
add PBD id to context
rohdesamuel Jan 31, 2023
1cb08f9
merge
Jan 31, 2023
566c7b8
Add more tests and spotless
Jan 31, 2023
7395a2e
Finish Java data sampling impl with tests, adding comments
Feb 1, 2023
4c1253f
more comments, remove Payload
Feb 1, 2023
3d88254
more comments
Feb 1, 2023
5bbef91
spotless
Feb 1, 2023
6993b83
Encode in the nested context
rohdesamuel Feb 3, 2023
769902f
Update sdks/java/harness/src/main/java/org/apache/beam/fn/harness/con…
rohdesamuel Feb 7, 2023
5c06d4e
Apply suggestions from code review
rohdesamuel Feb 7, 2023
99087e8
address pr comments
Feb 9, 2023
5609e4a
give default pbd id to test context
rohdesamuel Feb 9, 2023
694c929
address spotlesscheck
rohdesamuel Feb 9, 2023
519aece
spotless apply
rohdesamuel Feb 9, 2023
8efbb15
style guide spotless apply
rohdesamuel Feb 9, 2023
cd3732d
add serviceloader
rohdesamuel Feb 9, 2023
415e3f0
change datasampling to modify the consumers and not graph for sampling
rohdesamuel Feb 10, 2023
553fc5e
remove redundant SamplerState obj
rohdesamuel Feb 10, 2023
e260eb4
spotless
rohdesamuel Feb 10, 2023
4f29308
replace mutex with atomics in output sampler to reduce contention
rohdesamuel Feb 10, 2023
8cbc6c8
spotless and fix OutputSamplerTest
rohdesamuel Feb 10, 2023
f67234f
Update sdks/java/harness/src/main/java/org/apache/beam/fn/harness/dat…
rohdesamuel Feb 13, 2023
6815330
Update sdks/java/harness/src/main/java/org/apache/beam/fn/harness/dat…
rohdesamuel Feb 13, 2023
4848725
Update sdks/java/harness/src/main/java/org/apache/beam/fn/harness/deb…
rohdesamuel Feb 13, 2023
6c16576
always init outputsampler
rohdesamuel Feb 13, 2023
822587d
add final to DataSampler in FnHarness
rohdesamuel Feb 13, 2023
fe9ab2a
spotless apply
rohdesamuel Feb 13, 2023
07d37ea
update from proto names
rohdesamuel Feb 13, 2023
5e9c4b0
spotless bugs
Feb 14, 2023
f5f97fb
Apply suggestions from code review
rohdesamuel Feb 14, 2023
c3db7c0
address pr comments
Feb 14, 2023
fce6d69
spotlessapply and add byte[] test
Feb 15, 2023
69d8bb4
validate datasampler args
Feb 15, 2023
be2ebe4
add concurrency tests
Feb 15, 2023
93af06e
Merge branch 'master' into data-sampling-java
lukecwik Feb 21, 2023
cbdbbc3
Update sdks/java/harness/src/main/java/org/apache/beam/fn/harness/deb…
rohdesamuel Feb 21, 2023
8cc8ee0
Update sdks/java/harness/src/test/java/org/apache/beam/fn/harness/deb…
rohdesamuel Feb 21, 2023
10aa7de
Update sdks/java/harness/src/test/java/org/apache/beam/fn/harness/deb…
rohdesamuel Feb 21, 2023
04c6c88
Update sdks/java/harness/src/test/java/org/apache/beam/fn/harness/deb…
rohdesamuel Feb 21, 2023
862439b
improve contention tests
Feb 22, 2023
490be4a
spotless
Feb 22, 2023
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
Prev Previous commit
Next Next commit
address pr comments
  • Loading branch information
Sam Rohde authored and rohdesamuel committed Feb 13, 2023
commit 99087e8577220ca8d2fc0518cf3c5f4fa90c85dc
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.beam.model.fnexecution.v1.BeamFnApi;
import org.apache.beam.model.fnexecution.v1.BeamFnApi.SampleDataResponse.ElementList;
import org.apache.beam.model.fnexecution.v1.BeamFnApi.SampledElement;
import org.apache.beam.sdk.coders.Coder;
import org.apache.beam.vendor.grpc.v1p48p1.com.google.protobuf.ByteString;
import org.apache.beam.vendor.guava.v26_0_jre.com.google.common.collect.ImmutableSet;

/**
* The DataSampler is a global (per SDK Harness) object that facilitates taking and returning
Expand All @@ -38,8 +36,13 @@
*/
public class DataSampler {

/** Creates a DataSampler to sample every 10 elements while keeping a maximum of 10 in memory. */
public DataSampler() {}
/**
* Creates a DataSampler to sample every 1000 elements while keeping a maximum of 10 in memory.
*/
public DataSampler() {
this.maxSamples = 10;
this.sampleEveryN = 1000;
rohdesamuel marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @param maxSamples Sets the maximum number of samples held in memory at once.
Expand All @@ -51,17 +54,15 @@ public DataSampler(int maxSamples, int sampleEveryN) {
}

// Maximum number of elements in buffer.
private final int maxSamples = 10;
private final int maxSamples;

// Sampling rate.
private final int sampleEveryN = 1000;
private final int sampleEveryN;

// The fully-qualified type is: Map[ProcessBundleDescriptorId, [PCollectionId, OutputSampler]].
// The DataSampler object lives on the same level of the FnHarness. This means that many threads
// can and will
// access this simultaneously. However, ProcessBundleDescriptors are unique per thread, so only
// synchronization
// is needed on the outermost map.
// can and will access this simultaneously. However, ProcessBundleDescriptors are unique per
// thread, so only synchronization is needed on the outermost map.
private final Map<String, Map<String, OutputSampler<?>>> outputSamplers =
new ConcurrentHashMap<>();

Expand All @@ -73,8 +74,8 @@ public DataSampler(int maxSamples, int sampleEveryN) {
* @param processBundleDescriptorId The PBD to sample from.
* @param pcollectionId The PCollection to take intermittent samples from.
* @param coder The coder associated with the PCollection. Coder may be from a nested context.
* @return the OutputSampler corresponding to the unique PBD and PCollection.
* @param <T> The type of element contained in the PCollection.
* @return the OutputSampler corresponding to the unique PBD and PCollection.
*/
public <T> OutputSampler<T> sampleOutput(
String processBundleDescriptorId, String pcollectionId, Coder<T> coder) {
Expand All @@ -99,8 +100,8 @@ public BeamFnApi.InstructionResponse.Builder handleDataSampleRequest(

Map<String, List<byte[]>> responseSamples =
samplesFor(
ImmutableSet.copyOf(sampleDataRequest.getProcessBundleDescriptorIdsList()),
ImmutableSet.copyOf(sampleDataRequest.getPcollectionIdsList()));
sampleDataRequest.getProcessBundleDescriptorIdsList(),
sampleDataRequest.getPcollectionIdsList());

BeamFnApi.SampleDataResponse.Builder response = BeamFnApi.SampleDataResponse.newBuilder();
for (String pcollectionId : responseSamples.keySet()) {
Expand All @@ -124,7 +125,8 @@ public BeamFnApi.InstructionResponse.Builder handleDataSampleRequest(
* @param pcollections Filters all PCollections on this set. If empty, allows all PCollections.
* @return a map from PCollection to its samples.
*/
public Map<String, List<byte[]>> samplesFor(Set<String> descriptors, Set<String> pcollections) {
private Map<String, List<byte[]>> samplesFor(
List<String> descriptors, List<String> pcollections) {
Map<String, List<byte[]>> samples = new HashMap<>();

// Safe to iterate as the ConcurrentHashMap will return each element at most once and will not
Expand All @@ -142,32 +144,11 @@ public Map<String, List<byte[]>> samplesFor(Set<String> descriptors, Set<String>
return;
}

samples.putIfAbsent(pcollectionId, Collections.EMPTY_LIST);
samples.putIfAbsent(pcollectionId, new ArrayList<>());
rohdesamuel marked this conversation as resolved.
Show resolved Hide resolved
samples.get(pcollectionId).addAll(outputSampler.samples());
});
});

return samples;
}

/** @return samples from all PBDs and all PCollections. */
public Map<String, List<byte[]>> allSamples() {
return samplesFor(ImmutableSet.of(), ImmutableSet.of());
}

/**
* @param descriptors PBDs to filter on.
* @return samples only from the given descriptors.
*/
public Map<String, List<byte[]>> samplesForDescriptors(Set<String> descriptors) {
return samplesFor(descriptors, ImmutableSet.of());
}

/**
* @param pcollections PCollection ids to filter on.
* @return samples only from the given PCollections.
*/
public Map<String, List<byte[]>> samplesForPCollections(Set<String> pcollections) {
return samplesFor(ImmutableSet.of(), pcollections);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,37 +33,36 @@
*/
public class OutputSampler<T> {
private final Coder<T> coder;
private final List<T> buffer = new ArrayList<>(maxElements);
private static final Logger LOG = LoggerFactory.getLogger(OutputSampler.class);

// Temporarily holds elements until the SDK receives a sample data request.
private final List<T> buffer;

// Maximum number of elements in buffer.
private int maxElements = 10;
private final int maxElements;

// Sampling rate.
private int sampleEveryN = 1000;
private final int sampleEveryN;

// Total number of samples taken.
private long numSamples = 0;

// Index into the buffer of where to overwrite samples.
private int resampleIndex = 0;

public OutputSampler(Coder<T> coder) {
this.coder = coder;
}

public OutputSampler(Coder<T> coder, int maxElements, int sampleEveryN) {
this(coder);
this.coder = coder;
this.maxElements = maxElements;
this.sampleEveryN = sampleEveryN;
this.buffer = new ArrayList<>(this.maxElements);
}

/**
* Samples every 1000th element or if it is part of the first 10 in the (local) PCollection.
rohdesamuel marked this conversation as resolved.
Show resolved Hide resolved
*
rohdesamuel marked this conversation as resolved.
Show resolved Hide resolved
* @param element the element to sample.
*/
public void sample(T element) {
public synchronized void sample(T element) {
// Only sample the first 10 elements then after every `sampleEveryN`th element.
numSamples += 1;
if (numSamples > 10 && numSamples % sampleEveryN != 0) {
Expand All @@ -87,8 +86,17 @@ public void sample(T element) {
*/
public List<byte[]> samples() {
rohdesamuel marked this conversation as resolved.
Show resolved Hide resolved
List<byte[]> ret = new ArrayList<>();

// Serializing can take a lot of CPU time for larger or complex elements. Copy the array here
// so as to not slow down the main processing hot path.
rohdesamuel marked this conversation as resolved.
Show resolved Hide resolved
List<T> copiedBuffer;
synchronized (this) {
copiedBuffer = new ArrayList<>(buffer);
clear();
}

ByteArrayOutputStream stream = new ByteArrayOutputStream();
rohdesamuel marked this conversation as resolved.
Show resolved Hide resolved
for (T el : buffer) {
for (T el : copiedBuffer) {
try {
// This is deprecated, but until this is fully removed, this specifically needs the nested
// context. This is because the SDK will need to decode the sampled elements with the
Expand All @@ -102,7 +110,6 @@ public List<byte[]> samples() {
}
}

clear();
return ret;
}

Expand Down
Loading