title: Slow chunk response in Play 2.2
body: <p>In my play-framework-based web application users can download all the rows of different database tables in csv or json format. Tables are relatively large (100k+ rows) and I am trying to stream back the result using chunking in Play 2.2. </p>
  
  <p>However the problem is although println statements shows that the rows get written to the Chunks.Out object, they do not show up in the client side! If I limit the rows getting sent back it will work, but it also has a big delay in the beginning which gets bigger if I try to send back all the rows and causes a time-out or the server runs out of memory. </p>
  
  <p>I use Ebean ORM and the tables are indexed and querying from psql doesn't take much time. Does anyone have any idea what might be the problem? </p>
  
  <p>I appreciate your help a lot!</p>
  
  <p>Here is the code for one of the controllers:</p>
  
  <pre><code>@SecureSocial.UserAwareAction
  public static Result showEpex() {
  
      User user = getUser();
      if(user == null || user.getRole() == null)
          return ok(views.html.profile.render(user, Application.NOT_CONFIRMED_MSG));
  
      DynamicForm form = DynamicForm.form().bindFromRequest();
      final UserRequest req = UserRequest.getRequest(form);
  
      if(req.getFormat().equalsIgnoreCase("html")) {
          Page&lt;EpexEntry&gt; page = EpexEntry.page(req.getStart(), req.getFinish(), req.getPage());
          return ok(views.html.epex.render(page, req));
      }
  
      // otherwise chunk result and send back
      final ResultStreamer&lt;EpexEntry&gt; streamer = new ResultStreamer&lt;EpexEntry&gt;();
      Chunks&lt;String&gt; chunks = new StringChunks() {
              @Override
              public void onReady(play.mvc.Results.Chunks.Out&lt;String&gt; out) {
  
                  Page&lt;EpexEntry&gt; page = EpexEntry.page(req.getStart(), req.getFinish(), 0);
                  ResultStreamer&lt;EpexEntry&gt; streamer = new ResultStreamer&lt;EpexEntry&gt;();
                  streamer.stream(out, page, req);
              }
      };
      return ok(chunks).as("text/plain");
  }
  </code></pre>
  
  <p>And the streamer:</p>
  
  <pre><code>public class ResultStreamer&lt;T extends Entry&gt; {
  
  private static ALogger logger = Logger.of(ResultStreamer.class);
  
  public void stream(Out&lt;String&gt; out, Page&lt;T&gt; page, UserRequest req) {
  
      if(req.getFormat().equalsIgnoreCase("json")) {
          JsonContext context = Ebean.createJsonContext();
          out.write("[\n");
          for(T e: page.getList())
              out.write(context.toJsonString(e) + ", ");
          while(page.hasNext()) {
              page = page.next();
              for(T e: page.getList())
                  out.write(context.toJsonString(e) + ", ");
          }
          out.write("]\n");
          out.close();
      } else if(req.getFormat().equalsIgnoreCase("csv")) {
          for(T e: page.getList())
              out.write(e.toCsv(CSV_SEPARATOR) + "\n");
          while(page.hasNext()) {
              page = page.next();
              for(T e: page.getList())
                  out.write(e.toCsv(CSV_SEPARATOR) + "\n");
          }
          out.close();
      }else {
          out.write("Invalid format! Only CSV, JSON and HTML can be generated!");
          out.close();
      }
  }
  
  
  public static final String CSV_SEPARATOR = ";";
  } 
  </code></pre>
  
  <p>And the model:</p>
  
  <pre><code>@Entity
  @Table(name="epex")
  public class EpexEntry extends Model implements Entry {
  
      @Id
      @Column(columnDefinition = "pg-uuid")
      private UUID id;
      private DateTime start;
      private DateTime finish;
      private String contract;
      private String market;
      private Double low;
      private Double high;
      private Double last;
      @Column(name="weight_avg")
      private Double weightAverage;
      private Double index;
      private Double buyVol;
      private Double sellVol;
  
      private static final String START_COL = "start";
      private static final String FINISH_COL = "finish";
      private static final String CONTRACT_COL = "contract";
      private static final String MARKET_COL = "market";
      private static final String ORDER_BY = MARKET_COL + "," + CONTRACT_COL + "," + START_COL;
  
      public static final int PAGE_SIZE = 100;
  
      public static final String HOURLY_CONTRACT = "hourly";
      public static final String MIN15_CONTRACT = "15min";
  
      public static final String FRANCE_MARKET = "france";
      public static final String GER_AUS_MARKET = "germany/austria";
      public static final String SWISS_MARKET = "switzerland";
  
      public static Finder&lt;UUID, EpexEntry&gt; find = 
              new Finder(UUID.class, EpexEntry.class);
  
      public EpexEntry() {
      }
  
      public EpexEntry(UUID id, DateTime start, DateTime finish, String contract,
              String market, Double low, Double high, Double last,
              Double weightAverage, Double index, Double buyVol, Double sellVol) {
          this.id = id;
          this.start = start;
          this.finish = finish;
          this.contract = contract;
          this.market = market;
          this.low = low;
          this.high = high;
          this.last = last;
          this.weightAverage = weightAverage;
          this.index = index;
          this.buyVol = buyVol;
          this.sellVol = sellVol;
      }
  
      public static Page&lt;EpexEntry&gt; page(DateTime from, DateTime to, int page) {
  
          if(from == null &amp;&amp; to == null)
              return find.order(ORDER_BY).findPagingList(PAGE_SIZE).getPage(page);
          ExpressionList&lt;EpexEntry&gt; exp = find.where();
          if(from != null)
              exp = exp.ge(START_COL, from);
          if(to != null)
              exp = exp.le(FINISH_COL, to.plusHours(24));
          return exp.order(ORDER_BY).findPagingList(PAGE_SIZE).getPage(page);
      }
  
      @Override
      public String toCsv(String s) {
          return id + s + start + s + finish + s + contract + 
                  s + market + s + low + s + high + s + 
                  last + s + weightAverage + s + 
                  index + s + buyVol + s + sellVol;   
      }
  </code></pre>