Sunday, 16 March 2014

Several approaches to processing events (part 2 - asynchronous processing with blocking queues and thread pool)

In the previous post we were solving problem with computing prime factors for numbers read from a large file.
We were taking a synchronous approach, which was quite easy to implement but very slow.
This time we'll try to go the asynchronous way using blocking queues and thread pool.

First of all, let's take a look at our Reader class:

public class Reader implements Runnable {
private final BlockingQueue<String> queue;
private final CountDownLatch latch;
public Reader(BlockingQueue<String> queue, CountDownLatch latch) {
this.queue = queue;
this.latch = latch;
}
void read(String fileName) throws Exception {
File file = new File(fileName);
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
queue.put(line);
}
br.close();
latch.countDown();
}
@Override
public void run() {
try {
read("numbers.txt");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
view raw Reader.java hosted with ❤ by GitHub
The reader reads each line and puts it into the blocking queue. Once it's finished, it informs the latch.

The LineProcessor class is more complicated:

public class LineProcessor implements Runnable {
private final PrimeFactorCounter primeFactorCounter = new PrimeFactorCounter();
private final BlockingQueue<String> inputQueue;
private final BlockingQueue<String> successQueue;
private final BlockingQueue<String> exceptionsQueue;
private final CountDownLatch inputLatch;
private final CountDownLatch outputLatch;
public LineProcessor(BlockingQueue<String> inputQueue, BlockingQueue<String> successQueue, BlockingQueue<String> exceptionsQueue, CountDownLatch inputLatch, CountDownLatch outputLatch) {
this.inputQueue = inputQueue;
this.successQueue = successQueue;
this.exceptionsQueue = exceptionsQueue;
this.inputLatch = inputLatch;
this.outputLatch = outputLatch;
}
@Override
public void run() {
process();
}
void process() {
while (true) {
String number = null;
try {
long latchCount = inputLatch.getCount();
int queueSize = inputQueue.size();
if (latchCount == 0 && queueSize == 0) {
outputLatch.countDown();
return;
}
number = inputQueue.poll(1, TimeUnit.SECONDS);
long value = Long.valueOf(number);
if (value > 0) {
long factor = primeFactorCounter.primeFactors(value);
successQueue.put(value + "," + factor + "\n");
}
} catch (Exception e) {
exceptionsQueue.add(number + ", " + e + "\n");
}
}
}
}
Let's take a closer look at the collaborators.

inputQueue is the queue that Reader is writing to.
successQueue and exceptionsQueue are the queues that will be populated based on the line processing result.
inputLatch is the latch modified by the Reader.
outputLatch will be informed when there are no more lines to be processed.

The LineProcessor checks if the Reader already finished by checking inputLatch and inputQueue.
If it didn't, it takes a line from the inputQueue and and populates appropriate queue with the result.
If it did finish, it informs outputLatch and terminates processing.

The Writer class is quite simple:

public class Writer implements Runnable {
private final String fileName;
private final BlockingQueue<String> queue;
private final CountDownLatch latch;
public Writer(BlockingQueue<String> queue, CountDownLatch latch, String fileName) {
this.queue = queue;
this.fileName = fileName;
this.latch = latch;
}
@Override
public void run() {
write();
}
void write() {
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(fileName, true))) {
while (true) {
long latchCount = latch.getCount();
int queueSize = queue.size();
if (latchCount == 0 && queueSize == 0) {
return;
}
String line = queue.poll(1, TimeUnit.SECONDS);
if (line != null) {
bufferedWriter.write(line);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
view raw Writer.java hosted with ❤ by GitHub
It takes the messages from the queue and writes it to a file.
It terminates, if the queue is empty and the latch was informed that there is no more messages.

The only thing left is the Bootstrap class that binds it all together:

public class Bootstrap {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final int THREAD_POOL_SIZE = 4;
static final String PRIME_FACTOR_FILE = "primeFactorCounter-threadPool.txt";
static final String EXCEPTIONS_FILE = "exceptions-threadPool.txt";
static final String SUMMARY_FILE = "summary-threadPool.txt";
private BlockingQueue<String> inputQueue = new ArrayBlockingQueue<>(10000);
private BlockingQueue<String> successQueue = new ArrayBlockingQueue<>(10000);
private BlockingQueue<String> exceptionsQueue = new ArrayBlockingQueue<>(10000);
private CountDownLatch inputLatch = new CountDownLatch(1);
private CountDownLatch outputLatch = new CountDownLatch(THREAD_POOL_SIZE);
public void start() throws Exception {
long startTime = System.currentTimeMillis();
Reader reader = new Reader(inputQueue, inputLatch);
Writer successWriter = new Writer(successQueue, outputLatch, PRIME_FACTOR_FILE);
Writer exceptionsWriter = new Writer(exceptionsQueue, outputLatch, EXCEPTIONS_FILE);
Collection<LineProcessor> lineProcessors = new ArrayList<>();
for (int i = 0; i < THREAD_POOL_SIZE; i++) {
lineProcessors.add(new LineProcessor(inputQueue, successQueue, exceptionsQueue, inputLatch, outputLatch));
}
ExecutorService lineProcessorExecutor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
for (LineProcessor lineProcessor : lineProcessors) {
lineProcessorExecutor.execute(lineProcessor);
}
new Thread(reader).start();
Thread successWriterThread = new Thread(successWriter);
successWriterThread.start();
Thread exceptionsWriterThread = new Thread(exceptionsWriter);
exceptionsWriterThread.start();
lineProcessorExecutor.shutdown();
successWriterThread.join();
exceptionsWriterThread.join();
String summary = String.format("thread pool processing, time taken %d ms", System.currentTimeMillis() - startTime);
logger.debug(summary);
FileUtils.writeStringToFile(new File(SUMMARY_FILE), summary);
}
}
view raw Bootstrap.java hosted with ❤ by GitHub
The latches and queues are initialized and then passed to the processing objects via constructor.
Line processors are wrapped in a thread pool.
Reader and two writers are started as threads. We then wait until all writers are finished and the summary is printed.

We can check the behavior with the following test:

public class ThreadPoolSystemTest {
@Before
public void setUp() {
new File(Bootstrap.PRIME_FACTOR_FILE).delete();
new File(Bootstrap.EXCEPTIONS_FILE).delete();
new File(Bootstrap.SUMMARY_FILE).delete();
}
@Test
public void shouldProcessFile() throws Exception {
// when
new Bootstrap().start();
// then
assertResultFile();
assertExceptionFile();
assertSummaryFile();
}
private void assertResultFile() throws IOException {
List<String> lines = FileUtils.readLines(new File(Bootstrap.PRIME_FACTOR_FILE));
assertThat(lines.size(), is(9900));
assertThat(lines.contains("5556634922133,5"), is(true));
}
private void assertExceptionFile() throws IOException {
File exceptionsFile = new File(Bootstrap.EXCEPTIONS_FILE);
assertThat(exceptionsFile.exists(), is(true));
String exceptionsFileContent = FileUtils.readFileToString(exceptionsFile);
assertThat(exceptionsFileContent, containsString("badData1"));
assertThat(exceptionsFileContent, containsString("badData2"));
}
private void assertSummaryFile() {
assertThat(new File(Bootstrap.SUMMARY_FILE).exists(), is(true));
}
}
On a machine with Core2 Duo 2.53GHz it takes ~43 seconds to process 10 000 numbers.
The whole project can be found at github.

Wednesday, 26 February 2014

Scheduling tasks with Spring (updated)

Spring provides an easy way to schedule tasks.

Let's say that we would like to have an information about current time printed on a console periodically.

The class that prints the time may look like this:

public class TimePrinter {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss");
private static final Logger LOGGER = LoggerFactory.getLogger(TimePrinter.class.getName());
public void reportCurrentTime() {
LOGGER.info("The time is now " + DATE_FORMAT.format(new Date()));
}
}

We need to define a class that will encapsulate the printing task:

public class ScheduledTask {
static final String THREAD_NAME = ScheduledTask.class.getName() + "!";
@Autowired private TimePrinter timePrinter;
@Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
Thread.currentThread().setName(THREAD_NAME);
timePrinter.printCurrentTime();
}
}
The @Scheduled annotation is the key here: the method reportCurrentTime is annotated by it, therefore it will be invoked every 5 seconds.
You can also specify cron expression. You can use fixedRateString parameter if you want to read it from properties file.
Please note setting of the thread name - it will be needed for the test.
Adding production code only for tests is generally not a good practice, but in this case it can also be used for monitoring purposes.

The spring configuration looks as following:

@Configuration
@EnableScheduling
@ComponentScan("pl.mjedynak")
public class AppConfig {
@Bean
public ScheduledTask scheduledTask() {
return new ScheduledTask();
}
@Bean
public TimePrinter timePrinter() {
return new TimePrinter();
}
}
view raw AppConfig.java hosted with ❤ by GitHub
To run it we need to create an invoker class:

public class App {
public static void main(String[] args) {
new AnnotationConfigApplicationContext(AppConfig.class);
}
}
view raw App.java hosted with ❤ by GitHub
Unfortunately there is no trivial way to test it automatically. We can do it the following way.
Let's create a test class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class ScheduledTaskIntegrationTest {
@Test
public void shouldInvokeTask() {
Callable<Boolean> threadWithNameExists =
() -> getAllStackTraces().keySet().stream().
anyMatch(t -> t.getName().equals(ScheduledTask.THREAD_NAME));
await().until(threadWithNameExists, is(true));
}
}

When we run the test, the spring context will start and the task will be invoked by scheduler.
We check if the thread with our name exists.
We do it for some time to avoid race condition - it may happen that verification method will be invoked before thread starts.
The whole project alongside with dependencies can be found on github.

Monday, 13 January 2014

Several approaches to processing events (part 1 - synchronous processing)

Let's consider the following problem that we have to solve:

We have a file with numbers (each line has one number).
Our goal is to process every number and count its prime factors.
We need to write the result along with the processed number in a separate file.
In case of any exception during processing we need to write the exception to a file together with the number that was processed by the time it occurred.
Apart from that we also need to write the summary of the time that we spent on processing.

The class that counts prime factors looks as following:

public class PrimeFactorCounter {
public long primeFactors(long number) {
long n = number > 0 ? number : -number;
long counter = 0;
for (int i = 2; i <= n / i; i++) {
while (n % i == 0) {
counter++;
n /= i;
}
}
if (n > 1) {
counter++;
}
return counter;
}
}
The simplest but not the optimal way to solve this would be to process each line synchronously. It could look like that:

public class Bootstrap {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
static final String PRIME_FACTOR_FILE = "primeFactorCounter-synchronous.txt";
static final String EXCEPTIONS_FILE = "exceptions-synchronous.txt";
static final String SUMMARY_FILE = "summary-synchronous.txt";
private final PrimeFactorCounter primeFactorCounter = new PrimeFactorCounter();
private final Reader reader = new Reader();
private final Writer writer = new Writer();
public void start() throws IOException {
long startTime = System.currentTimeMillis();
Iterable<String> numbers = reader.getNumbers("numbers.txt");
StringBuilder valuesWithPrimeFactorCounter = new StringBuilder();
StringBuilder exceptions = new StringBuilder();
for (String number : numbers) {
try {
long value = Long.valueOf(number);
if (value > 0) {
long factor = primeFactorCounter.primeFactors(value);
valuesWithPrimeFactorCounter.append(value).append(",").append(factor).append("\n");
}
} catch (Exception e) {
exceptions.append(number).append(", ").append(e).append("\n");
}
}
writeResults(startTime, valuesWithPrimeFactorCounter, exceptions);
}
private void writeResults(long startTime, StringBuilder valuesWithPrimeFactorCounter, StringBuilder exceptions) throws IOException {
writer.write(PRIME_FACTOR_FILE, valuesWithPrimeFactorCounter.toString());
writer.write(EXCEPTIONS_FILE, exceptions.toString());
String summary = "synchronous processing, time taken " + (System.currentTimeMillis() - startTime) + " ms";
logger.debug(summary);
writer.write(SUMMARY_FILE, summary);
}
}
We're reading lines from numbers.txt file and then we're processing each line in a for loop. At the end we're writing everything to three files.

The Reader and Writer classes are quite simple:

public class Reader {
public Iterable<String> getNumbers(String fileName) throws IOException {
String fileContent = FileUtils.readFileToString(new File(fileName));
return Splitter.on("\n").omitEmptyStrings().split(fileContent);
}
}
public class Writer {
public void write(String fileName, String content) throws IOException {
FileUtils.writeStringToFile(new File(fileName), content);
}
}
They're using Guava and Apache Commons dependencies that have following declaration:

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>15.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
view raw dep.xml hosted with ❤ by GitHub
We can check the results in a following test:

public class SynchronousProcessingSystemTest {
@Before
public void setUp() {
new File(Bootstrap.PRIME_FACTOR_FILE).delete();
new File(Bootstrap.EXCEPTIONS_FILE).delete();
new File(Bootstrap.SUMMARY_FILE).delete();
}
@Test
public void shouldProcessFile() throws Exception {
// when
new Bootstrap().start();
// then
assertResultFile();
assertExceptionsFile();
assertSummaryFile();
}
private void assertResultFile() throws IOException {
List<String> lines = FileUtils.readLines(new File(Bootstrap.PRIME_FACTOR_FILE));
assertThat(lines.size(), is(9900));
assertThat(lines.contains("5556634922133,5"), is(true));
}
private void assertExceptionsFile() throws IOException {
File exceptionsFile = new File(Bootstrap.EXCEPTIONS_FILE);
assertThat(exceptionsFile.exists(), is(true));
String exceptionsFileContent = FileUtils.readFileToString(exceptionsFile);
assertThat(exceptionsFileContent, containsString("badData1"));
assertThat(exceptionsFileContent, containsString("badData2"));
}
private void assertSummaryFile() {
assertThat(new File(Bootstrap.SUMMARY_FILE).exists(), is(true));
}
}
On a machine with Core2 Duo 2.53GHz it takes ~73 seconds to process 10 000 numbers.

The whole project can be found at github.

In next posts we'll take a look at other approaches to solve this problem.