While trying the OSGi PushStream library I felt that it was really slow. I have created two methods that do the same thing one using the PushStream and the other a simple BlockingQueue (see code below), the result is the following:
Queue needs 3 milliseconds to process 1000 events.
PushStream needs 31331 milliseconds to process 1000 events.
Why the PushStream is slower? What I am doing wrong?
Code
With PushStream:
public class TestPush{
#Test
public void testPushStream() throws Exception {
final PromiseFactory pf = new PromiseFactory(PromiseFactory.inlineExecutor());
final PushStreamProvider psp = new PushStreamProvider();
final SimplePushEventSource<Integer> source =
psp.buildSimpleEventSource(Integer.class).withQueuePolicy(QueuePolicyOption.BLOCK).build();
final Deferred<Instant> startD = pf.deferred();
final Deferred<Instant> endD = pf.deferred();
psp.createStream(source).onClose(() -> endD.resolve( Instant.now()) ).forEach((i) -> {
if (i == 0) {
startD.resolve( Instant.now() );
}
});
final Promise<Long> nbEvent = psp.createStream(source).count();
for (int i = 0; i < 1000; i++) {
source.publish(i);
}
source.endOfStream();
System.out.println("PushStream needs "
+ Duration.between( startD.getPromise().getValue(), endD.getPromise().getValue() ).toMillis()
+ " milliseconds to process " + nbEvent.getValue() + " events.");
}
With ArrayBlockingQueue:
#Test
public void testBlockingQueue() throws Exception {
final PromiseFactory pf = new PromiseFactory(PromiseFactory.inlineExecutor());
final Executor e = Executors.newFixedThreadPool(1);
final ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<>(32);
final Deferred<Instant> startD = pf.deferred();
final Deferred<Instant> endD = pf.deferred();
final Deferred<Integer> nbEvent = pf.deferred();
e.execute( () -> {
try {
Integer i = 0;
Integer last = 0;
do {
i = abq.take();
if (i == 0) {
startD.resolve(Instant.now());
} else if (i != -1) {
last = i;
}
}
while (i != -1);
endD.resolve(Instant.now());
nbEvent.resolve(last + 1);
}
catch (final InterruptedException exception) {
exception.printStackTrace();
}
});
for (int i = 0; i < 1000; i++) {
abq.put(i);
}
abq.put(-1);
System.out.println("Queue needs "
+ Duration.between( startD.getPromise().getValue(), endD.getPromise().getValue() ).toMillis()
+ " milliseconds to process " + nbEvent.getPromise().getValue() + " events.");
}
}
This is a fun question :)
Why the PushStream is slower? What I am doing wrong?
Thank you for not just assuming that the PushStream implementation sucks. In this case it is slower because (probably without realising) you asked it to be!
Part 1 - Buffering
By default PushStreams are buffered. This means that they include a queue into which events are placed before they are processed. Buffering therefore does a few things which negatively affect the speed of throughput.
It adds an extra queue/dequeue step into the pipeline
It adds an extra thread switch in the event processing
The default policy for a buffer is to return back pressure related to how full the buffer is.
In this case the vast majority of the slowdown is because of the back pressure. When you create a stream using psp.createStream(source) it is set up with a buffer of 32 elements and a linear back pressure policy based on the size of the buffer, returning one second when full, and 31 millis when it has one item in it. It is worth noting that 31 millis per element adds up to 30 seconds!
Importantly, the SimplePushEventSource always honours back pressure requests from the consumers that are added to it. This means that you may be pumping events into the SimplePushEventSource as fast as you can, but they will only be delivered as fast as they are requested by the pipeline.
If we remove the buffering from the push streams that you are creating then we get the following test:
#Test
public void testPushStream2() throws Exception {
final PromiseFactory pf = new PromiseFactory(PromiseFactory.inlineExecutor());
final PushStreamProvider psp = new PushStreamProvider();
final SimplePushEventSource<Integer> source =
psp.buildSimpleEventSource(Integer.class)
.withQueuePolicy(QueuePolicyOption.BLOCK)
.build();
final Deferred<Instant> startD = pf.deferred();
final Deferred<Instant> endD = pf.deferred();
psp.buildStream(source).unbuffered().build().onClose(() -> endD.resolve( Instant.now()) ).forEach((i) -> {
if (i == 0) {
startD.resolve( Instant.now() );
}
});
final Promise<Long> nbEvent = psp.buildStream(source).unbuffered().build().count();
for (int i = 0; i < 1000; i++) {
source.publish(i);
}
source.endOfStream();
System.out.println("PushStream needs "
+ Duration.between( startD.getPromise().getValue(), endD.getPromise().getValue() ).toMillis()
+ " milliseconds to process " + nbEvent.getValue() + " events.");
}
The result of running this (on my machine) is:
PushStream needs 39 milliseconds to process 1000 events.
This is obviously much closer to what you would expect, but it is still noticeably slower. Note that we could have still had some buffering, but tuned the PushbackPolicy. This would have given us faster throughput, but not quite as fast as this.
Part 2 - Pipeline lengths
The next thing to notice is that you are using an onClose() handler. This adds an extra stage into your push stream pipeline. You can actually move the onClose to be a result of the promise, decreasing the length of your pipeline (you only need to run it once).
#Test
public void testPushStream3() throws Exception {
final PromiseFactory pf = new PromiseFactory(PromiseFactory.inlineExecutor());
final PushStreamProvider psp = new PushStreamProvider();
final SimplePushEventSource<Integer> source =
psp.buildSimpleEventSource(Integer.class)
.withQueuePolicy(QueuePolicyOption.BLOCK)
.build();
final Deferred<Instant> startD = pf.deferred();
final Deferred<Instant> endD = pf.deferred();
psp.buildStream(source).unbuffered().build().forEach((i) -> {
if (i == 0) {
startD.resolve( Instant.now() );
}
});
final Promise<Long> nbEvent = psp.buildStream(source).unbuffered().build().count()
.onResolve(() -> endD.resolve( Instant.now()));
for (int i = 0; i < 1000; i++) {
source.publish(i);
}
source.endOfStream();
System.out.println("PushStream needs "
+ Duration.between( startD.getPromise().getValue(), endD.getPromise().getValue() ).toMillis()
+ " milliseconds to process " + nbEvent.getValue() + " events.");
}
The result of this version (on my machine) is:
PushStream needs 21 milliseconds to process 1000 events.
Part 3 - Multiplexing delivery
A key difference between the "raw array blocking queue" example and the PushStream example is that you actually create two PushStreams. The first does the work to capture the start time, the second to count the events. This forces the SimplePushEventSource to multiplex the events over multiple consumers.
What if we collapsed the behaviour into a single pipeline so that the SimplePushEventSource could use a fast-path delivery?
#Test
public void testPushStream4() throws Exception {
final PromiseFactory pf = new PromiseFactory(PromiseFactory.inlineExecutor());
final PushStreamProvider psp = new PushStreamProvider();
final SimplePushEventSource<Integer> source =
psp.buildSimpleEventSource(Integer.class)
.withQueuePolicy(QueuePolicyOption.BLOCK)
.build();
final Deferred<Instant> startD = pf.deferred();
final Deferred<Instant> endD = pf.deferred();
final Promise<Long> nbEvent = psp.buildStream(source).unbuffered().build()
.filter(i -> {
if (i == 0) {
startD.resolve( Instant.now() );
}
return true;
})
.count()
.onResolve(() -> endD.resolve( Instant.now()));
for (int i = 0; i < 1000; i++) {
source.publish(i);
}
source.endOfStream();
System.out.println("PushStream needs "
+ Duration.between( startD.getPromise().getValue(), endD.getPromise().getValue() ).toMillis()
+ " milliseconds to process " + nbEvent.getValue() + " events.");
}
The result of this version (on my machine) is:
PushStream needs 3 milliseconds to process 1000 events.
Summary
PushStreams are a fast, effective way to consume asynchronously arriving events, but it is very important to understand about what buffering behaviour is suitable for your application. If you have a big lump of data that you want to iterate over very quickly then you need to be careful how you set things up, as the buffering defaults are designed for a different use case!
Related
I hope that someone could please help me. I have setup a simple test to test query performance for a basic graph. I have included the code below. Basically what I do is create 30000 vertices and then create 20000 edges on one of the vertices.
public async Task<bool> runTests(Models.Person person)
{
try
{
var gremlinServer = new GremlinServer(_hostname, _port, enableSsl: true,
username: "/dbs/" + _dataBaseId + "/colls/" + _collectionId,
password: _authKey);
using (var gremlinClient = new GremlinClient(gremlinServer, new GraphSON2Reader(), new GraphSON2Writer(), GremlinClient.GraphSON2MimeType))
{
await gremlinClient.SubmitAsync<dynamic>("g.E().drop()");
await gremlinClient.SubmitAsync<dynamic>("g.V().drop()");
var counter = 30000;
while (counter != 0)
{
await gremlinClient.SubmitAsync<dynamic>("g.addV('partition" + counter + "').property('id', 'partition" + counter + "').property('profilePicture', '" + person.ProfilePicture + "').property('name', 'Person " + counter + "').property('partitionKey', 'partition" + counter + "')");
counter--;
}
var counter = 20000;
while (counter != 0)
{
int num = counter + 1;
var personToLink = "partition" + num;
await gremlinClient.SubmitAsync<dynamic>("g.V('partition1').addE('friendsWith').to(g.V('partition" + num + "'))");
counter--;
}
var searchResults = await gremlinClient.SubmitAsync<dynamic>("g.V().hasId('partition1').out('friendsWith').order().by('name', incr).valueMap('name', 'profilePicture').range(0,2)");
return true;
}
}
catch (Exception ex)
{
throw ex;
}
}
When I run the following query results are returned quickly:
g.V().hasId('partition1').out('friendsWith').valueMap('name', 'profilePicture').range(0,2)
However, as soon as I add an order clause the query takes much, much longer. Over a minute to complete:
g.V().hasId('partition1').out('friendsWith').order().by('name', incr).valueMap('name', 'profilePicture').range(0,2)
Is there a way to index the graph to speed this type of query up?
I have another question as well, I have set the throughput to 5000 RU, however when I run a query that run very quickly I get the following:
Data Explorer Query Stats Result
What is this value supposed to represent (RU's?) if so why is it so high?
Also when I try to run a simple query:
g.V().hasId('partition1').out('friendsWith').hasId('partition20001')
I get "Request rate is large" even though this is such a simple query. Even more concerning is when I increase the throughput to 5000 RU's I do get a result back but its really slow, takes about 5-6 seconds for what should be a really simple query.
Is it possible to render in one progressbar the progress of 2 nested tasks?
I mean, if I have to read lines from several files in several directories. I have this snippet code:
EDIT:
final int steps = directories.size();
Task<Integer> task2 = new Task<Integer>() {
/**
*/
#Override
protected Integer call() throws Exception {
int len = files.length;
updateProgress(0, len);
for(int i = 0; i < files.length; i++) {
final String filename = files[i].getName();
final String file = files[i].getPath();
try {
BufferedReader br = new BufferedReader(new FileReader(file));
}
String currentLine = null;
while ((currentLine = br.readLine()) != null) {
readlines(currentLine, filename);
}
} catch (IOException xs) {
xs.getMessage();
}
System.out.println("******* Working on: " + file + " ******************************");
updateProgress(i + 1, files.length);
}
System.out.println("done: " + directoryName);
return len;
}
};
task2.setOnRunning(r -> {
DoubleBinding totalProgress = null;
for(int i = 0; i < steps; i++) {
DoubleBinding increment = task2.progressProperty().divide(steps);
if (totalProgress == null) {
totalProgress = increment;
} else {
totalProgress = totalProgress.add(increment);
}
}
progressBar.progressProperty().bind(totalProgress);
status.textProperty().bind(task2.stateProperty().asString());
statusProcess.textProperty().bind(Bindings.when(totalProgress.isEqualTo(-1))
.then("0.00")
.otherwise(task2.progressProperty().multiply(100.00).asString("%.02f %%")));
console.textProperty().bind(task2.messageProperty());
});
new Thread(task2).start();
I am enable to access progress bar but as long as I have directories, the progress bar will restart to 0 and will check for files into the next directory and then populate the progress bar until 1 and then restart to 0 if there is another directory.
How can I do if I want to display these 2 tasks in one as I don't want to restart from 0 when the next directory is reading. If I have for example 4 directories, I expect to see a moving the progressbar until its quarter and so on.
Is anybody as an idea what I am doing wrong?
You can do something like
progressBar.progressProperty().bind(Bindings.createDoubleBinding(
() -> Math.max(0, task1.getProgress()) / 2 + Math.max(0, task2.getProgress()) /2 ,
task1.progressProperty(), task2.progressProperty());
If you can easily estimate or compute the relative times each task will take, you can weight the progress of each task accordingly, instead of simply averaging them as in the code above.
I want to use Retrofit w/ RxJava to make API requests for items that are a certain distance from a given location. For illustation, this is how it might it may look for Retrofit w/o RxJava:
API api = ...
List<Item> items = new ArrayList<Item>();
int miles = 50;
do {
items = api.getItems(lon, lat, distance);
miles += 50;
} while (items.size() == 0);
Essentially, we'll keep increasing the distance until we get a response that has at least 1 item.
What's the best way to handle this kind of workflow with RxJava?
You can a combination of range, concatMap and filter for this purpose:
static Observable<Integer> getItems(int distance) {
if (distance < 500) {
return Observable.<Integer>empty().delay(500, TimeUnit.MILLISECONDS);
}
return Observable.just(1).delay(500, TimeUnit.MILLISECONDS);
}
public static void main(String[] args) {
Observable
.range(1, 20)
.map(v -> 50 * v)
.concatMap(d -> getItems(d).toList())
.doOnNext(list -> System.out.println("Got a list of " + list.size() + " items"))
.filter(list -> !list.isEmpty())
.first()
.toBlocking()
.forEach(System.out::println);
;
}
I need to measure that rate at which a software system is consuming messages from a message queue and report on that periodically.
Specifically, messages arrive from a message queueing system and I need to report (each second) on the number of messages received within a number of rolling windows - e.g. the last second, the last 5 seconds, the last 30 seconds, etc.
Whilst I'm sure I could build this, I'm not certain that I'd go about it in the most efficient manner! I'm also sure that there are libraries for doing this (I'm using the JVM, so Apache Commons Math springs to mind), but I don't even know the right words to Google for! :-)
Here is my solution based on exponential smoothing. It doesn't require any background threads. You would create 1 instance for each rolling window that you want to track. For each relevant event you would call newEvent on each instance.
public class WindowedEventRate {
private double normalizedRate; // event rate / window
private long windowSizeTicks;
private long lastEventTicks;
public WindowedEventRate(int aWindowSizeSeconds) {
windowSizeTicks = aWindowSizeSeconds * 1000L;
lastEventTicks = System.currentTimeMillis();
}
public double newEvent() {
long currentTicks = System.currentTimeMillis();
long period = currentTicks - lastEventTicks;
lastEventTicks = currentTicks;
double normalizedFrequency = (double) windowSizeTicks / (double) period;
double alpha = Math.min(1.0 / normalizedFrequency, 1.0);
normalizedRate = (alpha * normalizedFrequency) + ((1.0 - alpha) * normalizedRate);
return getRate();
}
public double getRate() {
return normalizedRate * 1000L / windowSizeTicks;
}
}
This is what I ended up writing.
package com.example;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BucketCounter {
private final Lock rollLock = new ReentrantLock();
private final int[] bucketSizes;
private final int[] buckets;
private final int[] intervals;
private final AtomicInteger incoming = new AtomicInteger(0);
public BucketCounter(int... bucketSizes) {
if (bucketSizes.length < 1) {
throw new IllegalArgumentException("Must specify at least one bucket size");
}
this.bucketSizes = bucketSizes;
this.buckets = new int[bucketSizes.length];
Arrays.sort(bucketSizes);
if (bucketSizes[0] < 1) {
throw new IllegalArgumentException("Cannot have a bucket of size < 1");
}
intervals = new int[bucketSizes[bucketSizes.length - 1]];
}
public int count(int n) {
return incoming.addAndGet(n);
}
public int[] roll() {
final int toAdd = incoming.getAndSet(0);
rollLock.lock();
try {
final int[] results = new int[buckets.length];
for (int i = 0, n = buckets.length; i < n; i++) {
results[i] = buckets[i] = buckets[i] - intervals[bucketSizes[i] - 1] + toAdd;
}
System.arraycopy(intervals, 0, intervals, 1, intervals.length - 1);
intervals[0] = toAdd;
return results;
} finally {
rollLock.unlock();
}
}
}
Initialise it by passing the different time increments (e.g. 1, 5, 30). Then arrange for a background thread to call roll() every "time period". If you call it every second, then your buckets are 1, 5 and 30 seconds. If you call it every 5 seconds, then your buckets are 5, 25 and 150 seconds, etc. Basically, the buckets are expressed in "number of times roll() is called").
roll() also returns you an array of the current counts for each bucket. Note that these numbers are the raw counts, and are not averaged per time interval. You'll need to do that division yourself if you want to measure "rates" rather than "counts".
Finally, every time an event happens, call count(). I've set up a system with a few of these and I call count(1) on each message to count incoming messages, count(message.size()) on each message to count incoming byte rates, etc.
Hope that helps.
You could probably implement it as an interceptor, so search for interceptor combined with the message queue product name and the language name.
I am facing a problem in modifying an object in a loop in the same thread and the modification seems to work fine if the modification is done in different threads.
Here is the code that I have written for the same.
The parent method call is:
public void task(){
List<URLTask> tasks = dService.getTasksForStatusReport(System.currentTimeMillis());
System.out.println("scheduler called to send mail");
this.sendMail(tasks,false);
}
service to fetch the list of the tasks:
public List<URLTask> getTasksForStatusReport(long timestamp) {
PersistenceManager pm = pmf.getPersistenceManager();
Query q = pm.newQuery("SELECT FROM " + URLTask.class.getName() + " WHERE nextMailTimestamp <= "+timestamp+" && isDeleted != true");
List<URLTask> c = (List<URLTask>)q.execute();
pm.detachCopyAll(c);//I have tried commenting and uncommenting this line but no effect
return c;
}
The code to for the sendMail(tasks,boolean) is as follow:
public void sendMail(List<URLTask> tasks , boolean consolidatedEmail){
for(URLTask task: tasks){
task.setNextMailTimestamp(System.currentTimeMillis() + task.getMailIntervalInMilliseconds());
dService.saveUrlTask(task);
}
}
}
saveUrlTask(task) method is:
public URLTask saveUrlTask(URLTask task) {
PersistenceManager pm = pmf.getPersistenceManager();
Transaction tx = pm.currentTransaction();
URLTask taskCopy;
tx.begin();
Query q = pm.newQuery("SELECT FROM " + URLTask.class.getName() +
" WHERE isDeleted != 0 && id == \"" + task.getId() + "\"");
long bfr = System.currentTimeMillis();
List<URLTask> taskList = (List<URLTask>)q.execute();
long aft = System.currentTimeMillis();
System.out.println("getting url task time = "+ (aft-bfr));
URLTask oldTask = taskList.get(0);
oldTask.setRepeatIntervalInSeconds(task.getRepeatIntervalInSeconds());
oldTask.setUrl(task.getUrl());
oldTask.setDeleted(task.isDeleted());
oldTask.setNextMailTimestamp(task.getNextMailTimestamp());
oldTask.setMailIntervalInMilliseconds(task.getMailIntervalInMilliseconds());
oldTask.setUserDefinedErrorMessages(task.getUserDefinedErrorMessages());
oldTask.setUpdateTimeStamp(System.currentTimeMillis());
oldTask.setMailIntervalInMilliseconds(task.getMailIntervalInMilliseconds());
oldTask.setNextMailTimestamp(task.getNextMailTimestamp());
oldTask.setUserDefinedErrorMessages(task.getUserDefinedErrorMessages());
bfr = System.currentTimeMillis();
pm.makePersistent(oldTask);
aft = System.currentTimeMillis();
System.out.println("updating url task time = "+ (aft-bfr));
taskCopy = (URLTask)(pm.detachCopy(oldTask));
tx.commit();
pm.close();
return taskCopy;
}
The code works perfectly when the size of the list is 1 and as the size is 2 it again works fine for the first one but throws the exception when runs for the second time.
For reference the exception is:
Exception in thread "HBase Connection Evictor" java.util.concurrent.RejectedExecutionException
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1768)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:767)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:658)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:92)
at org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation.processBatch(HConnectionManager.java:1143)
at org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation.processBatchOfPuts(HConnectionManager.java:1241)
at org.apache.hadoop.hbase.client.HTable.flushCommits(HTable.java:826)
at org.apache.hadoop.hbase.client.HTable.close(HTable.java:838)
at org.datanucleus.store.hbase.HBaseManagedConnection.closeTables(HBaseManagedConnection.java:169)
at org.datanucleus.store.hbase.HBaseManagedConnection.dispose(HBaseManagedConnection.java:154)
at org.datanucleus.store.hbase.HBaseConnectionPool.disposeTimedOutConnections(HBaseConnectionPool.java:94)
at org.datanucleus.store.hbase.HBaseConnectionPool.access$000(HBaseConnectionPool.java:27)
at org.datanucleus.store.hbase.HBaseConnectionPool$1.run(HBaseConnectionPool.java:105)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
Any form of help would be highly appreciable.
Thanks in advance!!
devsri