Nella mia applicazione Web basata su framework di gioco, gli utenti possono scaricare tutte le righe di diverse tabelle di database in formato csv o json. Le tabelle sono relativamente grandi (100k + righe) e sto cercando di riprodurre il risultato utilizzando il chunking in Play 2.2.Risposta a blocchi lenti in Play 2.2
Tuttavia il problema è che le istruzioni println mostrano che le righe vengono scritte nell'oggetto Chunks.Out, non vengono visualizzate nel lato client! Se limito il ritorno delle righe, funzionerà, ma ha anche un grosso ritardo all'inizio che diventa più grande se provo a rimandare tutte le righe e provoca un timeout o il server esaurisce la memoria.
Io uso Ebean ORM e le tabelle sono indicizzate e l'esecuzione di query da psql non richiede molto tempo. Qualcuno ha qualche idea di quale potrebbe essere il problema?
Apprezzo molto il tuo aiuto!
Ecco il codice per uno dei controller:
@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<EpexEntry> 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<EpexEntry> streamer = new ResultStreamer<EpexEntry>();
Chunks<String> chunks = new StringChunks() {
@Override
public void onReady(play.mvc.Results.Chunks.Out<String> out) {
Page<EpexEntry> page = EpexEntry.page(req.getStart(), req.getFinish(), 0);
ResultStreamer<EpexEntry> streamer = new ResultStreamer<EpexEntry>();
streamer.stream(out, page, req);
}
};
return ok(chunks).as("text/plain");
}
E lo Streamer:
public class ResultStreamer<T extends Entry> {
private static ALogger logger = Logger.of(ResultStreamer.class);
public void stream(Out<String> out, Page<T> 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 = ";";
}
E il modello:
@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<UUID, EpexEntry> 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<EpexEntry> page(DateTime from, DateTime to, int page) {
if(from == null && to == null)
return find.order(ORDER_BY).findPagingList(PAGE_SIZE).getPage(page);
ExpressionList<EpexEntry> 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;
}
Grazie per la vostra rispondi a Viktor. Il buffering migliorerà la velocità, tuttavia il ritardo tra quando scrivo e quando compare nel browser è ancora enorme. L'aggiunta di semplici istruzioni println mostra che tutte le righe verranno scritte all'esterno e quando non ci sono più e le uscite sono chiuse, iniziano a caricarsi nel browser !! E se il numero di righe è troppo grande c'è un errore di timeout come questo: – p00ya00
[ERRORE] [22/10/2013 13: 57: 16.285] [application-akka.actor.default-dispatcher-5] [ActorSystem (applicazione)] Impossibile eseguire il callback di terminazione, a causa di [Timeout scaduto dopo [5000 millisecondi]] java.util.concurrent.TimeoutException: i tempi scaduti dopo [5000 millisecondi] su scala.concurrent.impl.Promise $ DefaultPromise.ready (Promise.scala: 96) a scala.concurrent.impl.Promise $ DefaultPromise.result (Promise.scala: 100) a scala.concurrent.Await $$ anonfun $ result $ 1.apply (package.scala: 107) at akka.dispatch.MonitorableThreadFactory $ AkkaForkJoinWorkerThread $$ anon $ – p00ya00
Potrebbe inserire diversi 'System.out.println (System.currentTimeMillis())' nel codice e mostrare l'output qui? Posizionali dopo 'risultato statico ShowEpex()', dopo la riga '// altrimenti risultato chunk e invia back', appena prima dell'ultima riga di' stream pubblico vuoto (Out out, Pagina page, UserRequest req) 'e poco prima 'return ok (chunks) .as (" text/plain ");'? Per qualche ragione, l'esecuzione del blocco non è terminata o richiede così tanto tempo, quindi l'esecuzione è stata interrotta dal framework di gioco. Inoltre, hai provato a eseguire il mio codice? Potresti confermare se hai gli stessi problemi? –