Sending data in batches using spring integration?

Viewed 42

I am trying to read a file from GCP based on a notification received as per the flow defined below:

File reader - Deserialises the data into collection and sends for routing.

enter image description here

I am de-searializing the data in collection of objects and sending it router for further processing. As i don't have the control over file size, i am thinking of some approach of batching the reader process.

Currently, the file-reader service activator returns the whole Collection of deserialised objects.

Issue:

  • In case i receive a file of larger size i.e. with 200k records, i want to send this in batches to the header value router rather than a collection of 200k objects.

  • If i convert the file-reader into a splitter and add an aggregator after that Notification -> file-reader -> aggregator -> router. I would still need to return the collection of all the objects not the iterator.

  • I don't want to load all the record into a collection.

Updated approach:

   public <S> Collection<S> readData(DataInfo dataInfo, Class<S> clazz) {
        Resource gcpResource = context.getResource("classpath://data.json")
        var tempDataSet = new HashSet<S>();
        AtomicInteger pivot = new AtomicInteger();
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gcpResource.getInputStream()))) {
            bufferedReader.lines().map((dataStr) -> {
                try {
                    var data = deserializeData(dataStr, clazz);
                    return data;
                } catch (JsonProcessingException ex) {
                    throw new CustomException("PARSER-1001", "Error occurred while parsing", ex);
                }
            }).forEach(data -> {
                        if (BATCH_SIZE == pivot.get()) {
                            //When the size in tempDataSet reached BATCH_SIZE send the data in routing channel and reset the pivot
                            var message = MessageBuilder.withPayload(tempDataSet.clone())
                                    .setHeader(AppConstants.EVENT_HEADER_KEY, eventType)
                                    .build();
                            routingChannel.send(message);
                            pivot.set(0);
                            tempDataSet.removeAll(tempDataSet);
                        } else {
                            pivot.addAndGet(1);
                            tempDataSet.add(data);
                        }
                    });
            return tempDataSet;
        } catch (Exception ex) {
            throw new CustomException("PARSER-1002", "Error occurred while parsing", ex);
        }
    }

If the batch size in 100 and we received 1010 objects. The 11 batches would be created, 10 with 100 and last one with 10 objects in it.

In case i use a splitter and pass the stream to it, will it wait for the whole stream to finish and then send the collected collection or we can achieve something close to previous approach using it?

1 Answers

Not sure what is the question, but I would go with FileSplitter + Aggregator solution. The first one is exactly for streaming file reading use-case. The second one lets you to buffer incoming messages until they reach some condition, so it can emit a single message downstream. That message indeed could be with a collection as a payload.

Here is their docs for your consideration:

https://docs.spring.io/spring-integration/docs/current/reference/html/message-routing.html#aggregator

https://docs.spring.io/spring-integration/docs/current/reference/html/file.html#file-splitter

Related