0

I have a scenario where I am populating a DTO object using some logic. This DTO object has 8 objects. All these 8 objects are independent of each other. So, in my service class, I am currently calling all 8 setters methods in sequence and then returning the DTO object.

public PipelineAndActivitiesForDashboard customerpipeline(DashboardData payload) throws Exception
    {
        PipelineAndActivitiesForDashboard dashboardPipelineReturnData = new PipelineAndActivitiesForDashboard();
        List<LeadActivity> data = new ArrayList<LeadActivity>();
        Date fromdate = payload.getFromDate();
        Date todate = payload.getToDate();

        log.info("fromdate : " + fromdate + " todate: " + todate);
        data = lRepo.getActivity(fromdate, todate);

        log.info("Fetching Lead Generated Stats");
        dashboardPipelineReturnData.setLeadGenerated(// some logic to fetch values from data );

        log.info("Fetching Activities Created Stats");
        dashboardPipelineReturnData.setActivitiesCreated(// some logic to fetch values from data);

        log.info("Fetching Property Visit Stats");
        dashboardPipelineReturnData.setTotalPropertyVisit(// some logic to fetch values from data);

        log.info("Fetching Deal close Stats");
        dashboardPipelineReturnData.setDealClosed(// some logic to fetch values from data);

        log.info("Fetching deal lost Stats");
        dashboardPipelineReturnData.setDealLost(// some logic to fetch values from data);

        log.info("Fetching today's activities Stats");
        dashboardPipelineReturnData.setTodaysActivities(some logic to fetch values from data);

        log.info("Fetching pending activities Stats");
        dashboardPipelineReturnData.setPendingActivities(some logic to fetch values from data);

        log.info("Fetching upcoming activities Stats");
        dashboardPipelineReturnData.setUpcomingActivities(some logic to fetch values from data);

        return dashboardPipelineReturnData;

    }

Each of the logic takes around 5 seconds of processing. So, executing 8 in sequence is making the response slow.

I am looking for some help in identifying the java or spring concept that can be used here to trigger these 8 setter methods in parallel.

So, flow should be

  1. All the 8 setters should be triggerred in parallel
  2. Control should not reach to return statement until all 8 setter methods execution is complete
Sridhar Patnaik
  • 970
  • 1
  • 13
  • 24
  • Depending on the logic it might slow things down. As you are returning a (probably) largish list of results and do some in-memory filtering. Why not put all of that in a single query that directly creates the DTO you need. You have a database use it what it is good at (grouping, sorting and manipulating data). – M. Deinum Nov 16 '20 at 14:41
  • @M.Deinum All the 8 objects are independent and fetched using a different query criteria. hence, they cannot be merged to single query. However, agree that querying DB will be cheaper than doing in-memory calculation. Will definitely check feasibility and implement – Sridhar Patnaik Nov 16 '20 at 14:48
  • Depends on what those objects are, if they are counts, you can create a query with subqueries, if they are complex(er) objects you might need to make a smarter query and do some mapping on the client. But executing 8 additional queries is more or less a 1+N select issue of sorts – M. Deinum Nov 16 '20 at 14:59

1 Answers1

2

You can use Multithreading with cyclic barrier. Cyclic Barriers are used in programs in which we have a fixed number of threads that must wait for each other to reach a common point before continuing execution. You can create a Runnable job for every setter you need to process. Since we are using Cyclic barrier all the threads will run in parallel and will wait until all have completed the execution, before you can return the final object.

Pseudocode -

public class Test {

    public static void main(String[] args) {

        ExecutorService executors = Executors.newFixedThreadPool(8);
        CyclicBarrier barrier = new CyclicBarrier(8);

        executors.submit(new SetLeadGenerated(barrier,dashboardPipelineReturnData));
        //submit the runnable job for all other setter methods

        // at this point dashboardPipelineReturnData will be updated with all the data
    }

}

class SetLeadGenerated implements Runnable {

    private CyclicBarrier barrier;
    private PipelineAndActivitiesForDashboard  dashboardPipelineReturnData;

    public SetLeadGenerated(CyclicBarrier barrier,PipelineAndActivitiesForDashboard dashboardPipelineReturnData) {
    
        this.dashboardPipelineReturnData = dashboardPipelineReturnData;
        this.barrier = barrier;
    }

    @Override
    public void run()  {
   
        try {   
       //do your task here
       dashboardPipelineReturnData.setLeadGenerated(// some logic to fetch values from data );
        
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
        finally {
            barrier.await();
        }

       
    }

}

Once the number of threads that called await() equals numberOfThreads, the barrier then gives a way for the waiting threads

Shubham Chopra
  • 1,678
  • 17
  • 30
  • Thank you Shubham. Will try and get back – Sridhar Patnaik Nov 16 '20 at 14:38
  • 1
    If `dashboardPipelineReturnData.setLeadGenerated()` throws an exception, then `barrier.await()` for _that thread_ will not be called so the barrier will never be tripped, and the _other threads_ will never finish. Probably should move the `dashboardPipelineReturnData.setLeadGenerated()` inside the `try`, and have a `finally` that calls `barrier.await()`. – Andrew S Nov 16 '20 at 14:52
  • @AndrewS Yes you are right. Will update the code based on your suggestion. – Shubham Chopra Nov 16 '20 at 14:54
  • @ShubhamChopra run method is of type void, but we are returning an object. How can we return object where return type is void – Sridhar Patnaik Nov 16 '20 at 16:54
  • @SridharPatnaik Yes you are right, we do not need to return anything in this case.Since we are passing objects to our class, there is no need to return anything. If somehow you want to return something, you can use Callable instead of Runnable. – Shubham Chopra Nov 16 '20 at 17:47
  • @ShubhamChopra Thank you. Also, you know if we can use autowired object inside run method? I am trying to do but continuously facing NPE – Sridhar Patnaik Nov 17 '20 at 02:45
  • Spring does not control creation of runnable so you cannot use autowired object inside run method.Look at this for the workaround https://stackoverflow.com/questions/38026330/autowired-not-working-inside-a-runnable/51494061 – Shubham Chopra Nov 17 '20 at 03:01
  • Thanks @ShubhamChopra I was able to implement the solution using cyclicbarrier. However, facing strange issue. Object is being returned without all threads are completed. after every page refresh, i see a different pattern. in some case, onl first 6 setters are being executed and value is returned. while in some value is returned after 7th setter. any idea why is this happening? – Sridhar Patnaik Nov 17 '20 at 19:41
  • Screenshots after page refreshes - https://drive.google.com/drive/folders/1tlWrf5eT0MEE9Y_5ficERaQVcTFsn-A4?usp=sharing – Sridhar Patnaik Nov 17 '20 at 19:43
  • @SridharPatnaik, can you check if your setter method is throwing an exception. – Shubham Chopra Nov 18 '20 at 10:58