Hello,
@Jasha: advising a user to limit the amount of data usually does not
work. :)
On 2011-10-24 18:29, Paul Joseph wrote:
Actually the real reason is that the user is using a query that required
crafting to return 25K results. He is seeking to show the system "does
not scale."
Actually I am using in my XML template, a cocoon flowscript repeater
widget that does paging. But this is well after the Java logic has
built the large array of results (the array results and the vector
secureResults below)
I suspect what might be the problem:
1. Are your continuations session bound? They are not by default - even
though you set a short session expiry time most of the data is still
held by continuations which are being expired with totally different
mechanism. Container bound continuations are useful in only one case:
you build a session-less site. For "session" exprience ALWAYS use
session-bound-continuations.
2. Your flowscript continuation probably holds the ENTIRE rowset and
will hold it until it expires (and all children expire). You are not
supposed to put heavyweight object into your continuation.
Your solutions are:
a. use a generator instead of flowscript. Implementing own generator is
actually dead easy. Style your data with XSLT.
b. make flowscript "forget" the data after the report has been rendered.
You probably regenerate your rowset with each request anyway.
This one is actually also easy. This is an example of my production code:
/* TEMPLATE
var config = {
defaultOrderBy: "name",
defaultDirection: "asc",
valueListProvider: cocoon.getComponent( "valueListBeanName" ),
filterModelName: "filterModel",
viewName: "viewName",
errorRedirect: "/",
rowSelectionReturns: false,
bizData: bizData,
selections: selections,
formHandler: function ( action, bizData ) {
},
rowHandler: function( id, action, bizData ) {
}
}
*/
function sortedFilteredView( config ) {
var defaultMaxResults = new Integer( 100 );
var modelName = ( config.filterModelName != undefined ) ?
config.filterModelName : config.viewName;
var form = new Form( "cocoon:/form-def/" + modelName );
//form.createBinding("cocoon:/form-bind/" + config.filterModelName );
form.locale = determineLocale();
var model = form.getModel();
var bizData = ( config.bizData != undefined ) ? config.bizData : new
java.util.HashMap();
model.orderBy = config.defaultOrderBy;
model.direction = ( config.defaultDirection != undefined ) ?
config.defaultDirection : "asc";
model.maxResults = ( config.maxResults != undefined ) ?
config.maxResults : defaultMaxResults;
if ( config.formInitializer != undefined ) {
config.formInitializer( model );
}
model.skipResults = new Integer( 0 );
while ( true ) {
model.pageNo = new java.lang.Integer( model.skipResults /
model.maxResults + 1 );
var filterContext =
Packages.org.apache.cocoon.forms.util.ContainerWidgetAsMap( form.form );
var items = config.valueListProvider.generateResults(
filterContext, bizData );
var totalItemCount = config.valueListProvider.countEntries(
filterContext, bizData );
var pageCount = new java.lang.Integer( java.lang.Math.ceil(
totalItemCount / model.maxResults ) );
var firstPage = ( model.skipResults == 0 );
var lastPage = ( model.skipResults + model.maxResults >=
totalItemCount );
form.showForm( "form/" + config.viewName,
{ items : items,
orderBy :
model.orderBy,
direction:
model.direction,
maxResults:
model.maxResults,
pageNumber:
model.pageNo,
pageCount: pageCount,
firstPage: firstPage,
lastPage : lastPage,
viewConfig: config,
startIndex:
((model.pageNo - 1) * model.maxResults + 1),
bizData : bizData,
selections:
config.selections },
function() {
delete items;
}
);
if ( form.submitId == "finish" ) { //search
model.skipResults = new Integer( 0 );
} else if ( form.submitId == "cancel" ) {
return null;
} else if ( form.submitId == "next" ) {
model.skipResults = new Integer( model.skipResults +
model.maxResults );
} else if ( form.submitId == "prev" ) {
model.skipResults = new Integer( model.skipResults -
model.maxResults );
if ( model.skipResults < 0 )
model.skipResults = new Integer( 0 );
} else if ( form.submitId == "first" ) {
model.skipResults = new Integer( 0 );
} else if ( form.submitId == "last" ) {
model.skipResults = new java.lang.Integer( (
java.lang.Math.ceil( totalItemCount / model.maxResults ) - 1 ) *
model.maxResults );
} else if ( form.submitId == "changePage" ) {
if ( model.pageNo != null ) {
var skipCount = ( model.pageNo - 1 ) *
model.maxResults;
if ( skipCount < totalItemCount )
model.skipResults = new
Integer( skipCount );
else
model.skipResults = new
java.lang.Integer( ( java.lang.Math.ceil( totalItemCount / model.maxResults ) -
1 ) * model.maxResults );
if ( skipCount < 0 )
model.skipResults = new
Integer( 0 );
}
} else if ( form.submitId == "changeMaxResults" ) {
model.skipResults = new Integer( 0 );
} else if ( form.submitId != null ) {
if ( form.submitId == "selectRow" &&
config.rowSelectionReturns == true )
return model.rowId;
if ( config.rowHandler != undefined && model.rowId !=
null )
config.rowHandler( model.rowId, form.submitId,
bizData );
if ( config.formHandler != undefined && model.rowId ==
null )
config.formHandler( form.submitId, bizData );
}
if ( model.maxResults <= 0 )
model.maxResults = defaultMaxResults;
model.rowId = null;
}
}
The code is responsible for showing a pageable sortable filterable rowset.
The most important part is :
var items = config.valueListProvider.generateResults( filterContext,
bizData );
This object is VERY heavy (it might any row count you might like).
The next important thing is after rendering this object is deleted:
form.showForm( "form/" + config.viewName,
{ items : items,
orderBy :
model.orderBy,
direction:
model.direction,
maxResults:
model.maxResults,
pageNumber:
model.pageNo,
pageCount: pageCount,
firstPage: firstPage,
lastPage : lastPage,
viewConfig: config,
startIndex:
((model.pageNo - 1) * model.maxResults + 1),
bizData : bizData,
selections:
config.selections },
function() {
delete items;
}
);
There is a parameter for cocoon.sendPageAndWait which is not widely
known: a cleanup function.
http://cocoon.apache.org/2.1/userdocs/flow/api.html#cocoon
So your code has to look something like that:
var report = generateYourHorriblyBigReport();
cocoon.sendPageAndWait( "myreport.html",
{ report: report },
function() {
delete report;
}
);
I advice you to use some kind of profiler. Money spent on YourProfiler
might be one of best spent dollars. You can:
- make a memory snapshot
- do your request
- make a subsequent memory snapshot
- compare both snapshots: there should be NO significant memory
consumption difference.
HTH
lg
--
Leszek Gawron http://lgawron.posterous.com
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]