domenica 12 febbraio 2012

Alfresco Share datalist: Paginazione server-side

Durante lo sviluppo del modulo di protocollo a norma CNIPA su Alfresco Share (Beefly Protocol), ci siamo imbattuti in problemi di performance nella visualizzazione della datalist di Alfresco Share utilizzata per implementare il registro di protocollo (fig. seguente).



Tali problemi si presentavano quando il numero degli elementi della datalist raggiungeva il limite di 35-40.000 unità. L’effetto che si aveva era che il tempo di risposta del webscript che si occupa di restituire gli elementi della datalist era superiore al minuto, quindi la connessione client-server si chiudeva per timeout e il client, quindi l’interfaccia utente rimaneva “freezzata”.

Per risolvere questo problema abbiamo effettuato una analisi approfondita relativa alla gestione delle datalist da parte di Alfresco Share. Alfresco memorizza sul repository gli elementi di una datalist gestendoli come nodi contenuti in una folder. La folder rappresenta la datalist stessa, in sostanza è come avere una folder, e tanti figli ognuno rappresentante un elemento della datalist, quindi se sul repository si crea una cartella e in essa si caricano 35-40.000 files l’effetto della navigazione nella cartella dovrebbe essere lo stesso del caricamento della datalist, ma non è così!!! La navigazione, vale a dire il passaggio da una pagina all’altra, tra i figli della cartella sul repository è molto più responsiva e performante.

È bene ricordare che le impostazioni di default di Alfresco fanno si che lucene restituisca soltanto i primi 1000 elementi di ogni query (a meno che non si modifichino i parametri di configurazione), questo vale sia per le datalist che per i files contenuti nelle cartelle del repository.

I due webscript che si occupano di fare la query e restituire i risultati sono: data-post.json.js per la datalist e doclist.get.js per la documentLibrary. Analizzandoli con attenzione abbiamo notato che il tempo per eseguire la query è pressoché identico per entrambi ciò che effettivamente cambia è il numero di risultati restituiti al client. Nel caso della datalist al client vengono inviati tutti i 1000 elementi, ed in particolare per ognuno di questi elementi invocata la funzione Evaluator.run(node, fields) ciò causa notevoli rallentamenti e degrado dei tempi di risposta. In sostanza la paginazione sulle datalist e fatta sul client, la navigazione da una pagina all’altra non è altro che uno shift sull’array degli elementi che sono stati già tutti inviati al client, questo garantisce ottime prestazione nel passaggio da una pagina all’altra (tempo di risposta immediato) ma impedisce il caricamento degli elementi la prima volta che si visualizza la datalist.

Al contrario nella documentLibrary la paginazione è fatta sul server, ossia il passaggio da una pagina all’altra provoca una nuova chiamata al webscript doclist.get.js al quale vengono passati il numero della pagina e il numero degli elementi visualizzati nella pagina. Con queste informazioni lo shift sui risultati della query viene fatto sul server e la funzione Evaluator.run(node, fields) viene chiamata su un numero limitato di elementi (in particolare tanti quanti ne sono visualizzati in una pagina di solito 50) con un notevole miglioramento dei tempi di risposta.

Quindi per rendere le datalist più performanti si è deciso di applicare la stessa logica usata da Alfresco nella documentlibrary. Applicando questa logica si ottengono buoni risultati in termini di risposta al caricamento delle datalist anche con datalist che hanno 75-80.000 elementi.

Le modifiche coinvolgono due webscript. In particolare per il file datagrid.js di share (lato client) sono:

// Register History Manager page update callback
YAHOO.util.History.register("page", bookmarkedPage, function DataGrid_onHistoryManagerPageChanged(newPage)
{
Alfresco.logger.debug("HistoryManager: page changed:" + newPage);
me.widgets.paginator.setPage(parseInt(newPage, 10));

// Update the DataGrid
if (this.currentPage != newPage)
{
this._updateDataGrid.call(this,
{
page: newPage
});
}
else
{
Alfresco.logger.debug("...page changed event ignored.");
}

}, null, this);

In pratica si registra sulla funzione di cambio pagina, la funzione di updateDataGrid che permette di rifare la query. Le altre modifiche allo stesso file riguardano la definizione della tabella:

// DataTable definition
var me = this;
this.widgets.dataTable = new YAHOO.widget.DataTable(this.id + "-grid", columnDefinitions, this.widgets.dataSource,
{
renderLoopSize: this.options.usePagination ? 16 : 32,
initialLoad: false,
dynamicData: true,
"MSG_EMPTY": this.msg("message.empty"),
"MSG_ERROR": this.msg("message.error")
//paginator: this.widgets.paginator
});

// Update totalRecords with value from server
this.widgets.dataTable.handleDataReturnPayload = function DataGrid_handleDataReturnPayload(oRequest, oResponse, oPayload)
{
me.totalRecords = oResponse.meta.totalRecords;
/*oResponse.meta.pagination =
{
rowsPerPage: me.options.pageSize,
recordOffset: (me.currentPage - 1) * me.options.pageSize
}; */
return oResponse.meta;
};

e la formazione della query, in particolare alla funzione _buildSearchParams va aggiunto questa istruzione di if:

// Pagination in use?
if (this.options.usePagination)
{
obj.page = this.widgets.paginator.getCurrentPage() || this.currentPage;
obj.pageSize = this.widgets.paginator.getRowsPerPage();
}

Invece per quanto riguarda il file data.post.json.js che si trova in alfresco (lato server) le modifiche servono a gestire i nuovi parametri page e pageSize settati nel file datagrid-min.js.

Questo codice va aggiunto subito dopo l'esecuzione della query.

var pageSize, pagePos;

if (json.has("size")) {
pageSize = json.get("size");
}
else {
pageSize = allNodes.length;
}

if (json.has("pos")) {
pagePos = json.get("pos");
}
else {
pagePos = "1";
}

var nodes = [],
startIndex = (pagePos - 1) * pageSize;

nodes = allNodes.slice(startIndex, pagePos * pageSize);

for each (node in nodes)
{
try
{
items.push(Evaluator.run(node, fields));
}
catch(e) {}
}
}

Fatte queste modifiche le datalist saranno molto più performanti e veloci!!!

Nessun commento:

Posta un commento