On 26 March 2013 08:35, Danny Lade <[email protected]> wrote:
> Sorry guys,
>
> I've found the error in the code itself and not in the handling of sample
> senders.
>
> Hint: Never use static ThreadLocal (statisticalMapSelector) AND non-static
> attributes (statisticalMaps) to store data for threads.
> (removing the "static" from the ThreadLocal solved all the problems)
>
> One simple question is still open, when and why does JMeter opens different
> instances of SampleSender?
If you want to know when your class is instatiated, add the logging to it.
I tend to use something like:
log.debug("N", new Throwable());
where N is a unique number or string within the class.
> Tracking the data that had been processed at the instances gave me no clue
> about a logic behind it.
>
> G Danny
>
> -----original message-----
> Von: Danny Lade [mailto:[email protected]]
> Gesendet: Dienstag, 19. März 2013 10:45
> An: [email protected]
> Betreff: curiosities in SampleSender
>
> Dear JMeter-Community,
>
> there are two things I don't get using my own SampleSender (see attachement).
>
> 1. if I initialize the "statisticalMapSelector" (ThreadLocal) static I get
> no results at the client shown and the jmeter-server.log looks like :
>
> 2013/03/19 09:45:28 INFO -
> net.bigpoint.jmeter.samplers.StatisticalHoldSampleSender: Test Ended on host:
> 10.188.26.243
> 2013/03/19 09:45:28 INFO -
> net.bigpoint.jmeter.samplers.StatisticalHoldSampleSender: Process 110 events
> 2013/03/19 09:45:28 INFO -
> net.bigpoint.jmeter.samplers.StatisticalHoldSampleSender: Test Ended on host:
> 10.188.26.243
> 2013/03/19 09:45:28 INFO -
> net.bigpoint.jmeter.samplers.StatisticalHoldSampleSender: Process 0 events
> 2013/03/19 09:45:28 INFO - jmeter.engine.StandardJMeterEngine: Test has
> ended on host 10.188.26.243
>
> 2. if I clean up the data at testEnded() using "finally {
> statisticalMaps.clear(); }" I get no results at the client shown and the
> jmeter-server.log looks like above
>
> In all other cases this code works fine (as attached)
> -----source-----
> package net.bigpoint.jmeter.samplers;
>
> import static com.google.common.base.Objects.firstNonNull;
> import static com.google.common.base.Objects.toStringHelper;
> import static com.google.common.base.Preconditions.checkNotNull;
> import static com.google.common.base.Strings.emptyToNull;
>
> import java.io.ObjectStreamException;
> import java.io.Serializable;
> import java.rmi.RemoteException;
> import java.util.List;
> import java.util.Map;
>
> import org.apache.jmeter.samplers.AbstractSampleSender;
> import org.apache.jmeter.samplers.RemoteSampleListener;
> import org.apache.jmeter.samplers.SampleEvent;
> import org.apache.jmeter.samplers.SampleResult;
> import org.apache.jmeter.save.SaveService;
> import org.apache.jorphan.logging.LoggingManager;
> import org.apache.log.Logger;
>
> import com.google.common.base.Objects;
> import com.google.common.collect.Lists;
> import com.google.common.collect.Maps;
>
> public class StatisticalHoldSampleSender extends AbstractSampleSender
> implements Serializable {
>
> private static final long serialVersionUID = 1167910624585715712L;
>
> private static final Logger log = LoggingManager.getLoggerForClass();
>
> private final RemoteSampleListener listener;
>
> // note: it is necessary to initialize this value at readResolve() and
> NOT statically
> private static transient ThreadLocal<Map<StatisticalResult,
> StatisticalResult>> statisticalMapSelector;
>
> private transient List<Map<StatisticalResult, StatisticalResult>>
> statisticalMaps;
>
> /**
> * Constructor, only called by client code.
> *
> * @param listener that the List of sample events will be sent to.
> */
> public StatisticalHoldSampleSender(RemoteSampleListener listener) {
> this.listener = listener;
> }
>
> /**
> * @param event a Sample Event
> */
> @Override
> public void sampleOccurred(SampleEvent event) {
> StatisticalResult key = new StatisticalResult(event);
> StatisticalResult result = putIfAbsent(statisticalMapSelector.get(),
> key, key);
> result.update(event);
> }
>
> /**
> * Checks if any sample events are still present in the sampleStore and
> * sends them to the listener. Informs the listener of the testended.
> *
> * @param host the hostname that the test has ended on.
> */
> @Override
> public void testEnded(String host) {
> final String threadName = Thread.currentThread().getName();
> log.info("Test Ended on host: " + host);
> try {
> List<SampleEvent> events = Lists.newArrayList();
> for (Map<StatisticalResult, StatisticalResult>
> statisticalResultMap : statisticalMaps) {
> for (StatisticalResult statisticalResult :
> statisticalResultMap.values()) {
> if (log.isDebugEnabled()) {
> log.debug("# " + threadName + " - Add event for: " +
> statisticalResult);
> }
> events.add(createSampleEvent(statisticalResult));
> }
> statisticalResultMap.clear();
> }
> log.info("Process " + events.size() + " events");
> listener.processBatch(events);
> listener.testEnded(host);
> } catch (RemoteException err) {
> log.error("testEnded(hostname)", err);
> }
> }
>
> private static <K, V> V putIfAbsent(Map<K, V> map, K key, V value) {
> V originalValue = map.get(key);
> if (originalValue == null) {
> map.put(key, value);
> originalValue = value;
> if (log.isDebugEnabled()) {
> log.debug("# Add object to map: " + value);
> }
> }
> return originalValue;
> }
>
> static SampleEvent createSampleEvent(StatisticalResult statResult) {
> SampleResult sampleResult = new SampleResult(statResult.startTime,
> statResult.elapsed);
>
> sampleResult.setThreadName(statResult.threadName);
> sampleResult.setSampleLabel(statResult.label);
> sampleResult.setSampleCount(statResult.sampleCount);
> sampleResult.setDataType(statResult.sampleType);
> sampleResult.setBytes(statResult.requestSize);
> sampleResult.setErrorCount(statResult.errorCount);
> sampleResult.setResponseCode(statResult.responseCode);
> sampleResult.setLatency(statResult.latency);
> sampleResult.setEndTime(statResult.endTime);
>
> return new SampleEvent(sampleResult, statResult.threadGroup);
> }
>
> /**
> * Processed by the RMI server code; acts as testStarted().
> *
> * @throws ObjectStreamException
> */
> @SuppressWarnings("static-access")
> private Object readResolve() throws ObjectStreamException {
> this.statisticalMaps = Lists.newArrayList();
> this.statisticalMapSelector = new ThreadLocal<Map<StatisticalResult,
> StatisticalResult>>() {
> @Override
> protected java.util.Map<StatisticalResult, StatisticalResult>
> initialValue() {
> final Map<StatisticalResult, StatisticalResult>
> statisticalMap = Maps.newHashMap();
>
> statisticalMaps.add(statisticalMap);
> return statisticalMap;
> };
> };
>
> log.info(toStringHelper("running with ").toString());
> return this;
> }
>
> static class StatisticalResult {
>
> final String threadGroup;
>
> final String threadName;
>
> final String label;
>
> final String responseCode;
>
> final String sampleType;
>
> int sampleCount = 0;
>
> int errorCount = 0;
>
> int requestSize = 0;
>
> long startTime = Long.MAX_VALUE; // guarantee it is set with the
> first update()
>
> long endTime = 0;
>
> long latency = 0;
>
> long elapsed = 0;
>
> /**
> * Create a statistical sample result.
> */
> StatisticalResult(SampleEvent event) {
> // using of intern string pool guarantees precalculated hashes
> and less memory usage
> checkNotNull(event);
> SampleResult result = checkNotNull(event.getResult());
> this.threadGroup =
> firstNonNull(emptyToNull(event.getThreadGroup()), "<unknown group>").intern();
> this.threadName =
> firstNonNull(emptyToNull(result.getThreadName()), "<unknown
> thread>").intern();
> this.label = firstNonNull(emptyToNull(result.getSampleLabel()),
> "<unknown label>").intern();
> this.responseCode =
> firstNonNull(emptyToNull(result.getResponseCode()), "<unknown response
> code>").intern();
> this.sampleType =
> SaveService.classToAlias(result.getClass().getName());
> }
>
> void update(SampleEvent event) {
> checkNotNull(event);
> SampleResult result = checkNotNull(event.getResult());
> if (!result.isSuccessful()) {
> errorCount++;
> }
> sampleCount += result.getSampleCount();
> requestSize += result.getBytes();
> startTime = Math.min(startTime, result.getStartTime());
> endTime = Math.max(endTime, result.getEndTime());
> latency += result.getLatency();
> elapsed += result.getTime();
> }
>
> @Override
> public int hashCode() {
> return Objects.hashCode(responseCode, label, sampleType);
> }
>
> @Override
> public boolean equals(Object obj) {
> if (obj == null || this.getClass() != obj.getClass()) {
> return false;
> }
> final StatisticalResult other = (StatisticalResult) obj;
> return Objects.equal(this.responseCode, other.responseCode) &&
> Objects.equal(this.label, other.label)
> && Objects.equal(this.sampleType, other.sampleType);
> }
>
> @Override
> public String toString() {
> return toStringHelper(this).add("sampleType",
> sampleType).add("threadGroup", threadGroup).add("threadName",
> threadName).add("label", label)
> .add("responseCode", responseCode).add("sampleCount",
> sampleCount).add("errorCount", errorCount).add("requestSize", requestSize)
> .add("startTime", startTime).add("endTime",
> endTime).add("latency", latency).add("elapsedTime", elapsed).toString();
> }
> }
> }