Thank you for reporting this bug. We will make it our priority to review this report.
  1. #1
    Sencha User
    Join Date
    Oct 2011
    Posts
    20
    Vote Rating
    0
    nstokoe is on a distinguished road

      0  

    Default Live GroupSummary Grid renders slower as row total increases

    Live GroupSummary Grid renders slower as row total increases


    I have a Live GroupSummary Grid that is getting populated via a service call which invokes an EJB. Through logging messages I know that on average it takes 1 second to retrieve that data from the service call. But, the larger the size of the ArrayList that it returns that gets added to the ListStore, the longer the grid takes to render.

    For example, if I constrain the service call so that only returns 20 items, the grid renders in about 3 seconds total. With 60 items about 10 seconds. With no constraints the service returns 112 items and takes almost 30 secons to render. When using the max rows the browser even thinks that the script is stuck in an infinite loop and asks if I want to stop the script that is executing. I always click no because I know its not in a infinite loop and it always comes to an end, it just takes forever. The maximum number of groups based on the initial groupBy method is 3, but ususally there is only one group.

    Is there way I can change my code to improve performance or is this a bug?

    Below is a copy of the my view and presenter code (I am using the MVP pattern).

    BacklogAndThroughputView.java
    Code:
    package org.noaa.espc.nde.dhs.portal.mc.client.view;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    import com.google.gwt.cell.client.AbstractCell;
    import com.google.gwt.core.client.GWT;
    import com.google.gwt.core.client.Scheduler;
    import com.google.gwt.core.client.Scheduler.ScheduledCommand;
    import com.google.gwt.i18n.client.DateTimeFormat;
    import com.google.gwt.i18n.client.NumberFormat;
    import com.google.gwt.safehtml.shared.SafeHtml;
    import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
    import com.google.gwt.safehtml.shared.SafeHtmlUtils;
    import com.google.gwt.user.client.Window;
    import com.google.gwt.user.client.ui.IsWidget;
    import com.google.gwt.user.client.ui.Label;
    import com.google.gwt.user.client.ui.Widget;
    import com.sencha.gxt.core.client.ValueProvider;
    import com.sencha.gxt.data.shared.ListStore;
    import com.sencha.gxt.data.shared.SortDir;
    import com.sencha.gxt.data.shared.Store.StoreSortInfo;
    import com.sencha.gxt.widget.core.client.container.AbstractHtmlLayoutContainer.HtmlData;
    import com.sencha.gxt.widget.core.client.container.HtmlLayoutContainer;
    import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
    import com.sencha.gxt.widget.core.client.grid.ColumnModel;
    import com.sencha.gxt.widget.core.client.grid.Grid;
    import com.sencha.gxt.widget.core.client.grid.GroupSummaryView;
    import com.sencha.gxt.widget.core.client.grid.SummaryColumnConfig;
    import com.sencha.gxt.widget.core.client.grid.SummaryRenderer;
    import com.sencha.gxt.widget.core.client.grid.SummaryType;
    import com.sencha.gxt.widget.core.client.grid.filters.DateFilter;
    import com.sencha.gxt.widget.core.client.grid.filters.GridFilters;
    import com.sencha.gxt.widget.core.client.grid.filters.StringFilter;
    
    public class BacklogAndThroughputView implements IsWidget,
            BacklogAndThroughputPresenter.Display {
    
        private ListStore<GranuleThroughputByProduct> store;
        private static final GranuleThroughputByProductProperties props = GWT
                .create(GranuleThroughputByProductProperties.class);
        private GroupSummaryView<GranuleThroughputByProduct> summary;
    
        @Override
        public Widget asWidget() {
    
            // Config each column
            SummaryColumnConfig<GranuleThroughputByProduct, String> productNameCol = new SummaryColumnConfig<GranuleThroughputByProduct, String>(
                    props.productShortName(), 130, "Shortname");
            productNameCol.setSummaryType(new SummaryType.CountSummaryType());
            productNameCol
                    .setSummaryRenderer(new SummaryRenderer<GranuleThroughputByProduct>() {
                        @Override
                        public SafeHtml render(
                                Number value,
                                Map<ValueProvider<GranuleThroughputByProduct, ?>, Number> data) {
                            return SafeHtmlUtils
                                    .fromTrustedString(value.intValue() > 1 ? "("
                                            + value.intValue() + " Products)"
                                            : "(1 Product)");
                        }
                    });
    
            final SummaryColumnConfig<GranuleThroughputByProduct, String> datasource = new SummaryColumnConfig<GranuleThroughputByProduct, String>(
                    props.datasource(), 55, "Datasource");
    
            SummaryColumnConfig<GranuleThroughputByProduct, Integer> backlogCol = new SummaryColumnConfig<GranuleThroughputByProduct, Integer>(
                    props.backlogCount(), 60, "Backlog");
            backlogCol.setSummaryType(new SummaryType.SumSummaryType<GranuleThroughputByProduct, Integer>());
            backlogCol.setSummaryFormat(NumberFormat.getDecimalFormat());
    
            SummaryColumnConfig<GranuleThroughputByProduct, Long> ingest1XCol = new SummaryColumnConfig<GranuleThroughputByProduct, Long>(
                    props.server1count(), 60, "Last Hr Ingest 1");
            ingest1XCol.setSummaryType(new SummaryType.SumSummaryType<GranuleThroughputByProduct, Long>());
            ingest1XCol.setSummaryFormat(NumberFormat.getDecimalFormat());
            
            SummaryColumnConfig<GranuleThroughputByProduct, Long> ingest2XCol = new SummaryColumnConfig<GranuleThroughputByProduct, Long>(
                    props.server2count(), 60, "Last Hr Ingest 2");
            ingest2XCol.setSummaryType(new SummaryType.SumSummaryType<GranuleThroughputByProduct, Long>());
            ingest2XCol.setSummaryFormat(NumberFormat.getDecimalFormat());
            
            SummaryColumnConfig<GranuleThroughputByProduct, Date> lastIngestedCol = new SummaryColumnConfig<GranuleThroughputByProduct, Date>(
                    props.lastIngestedTime(), 100, "Last Ingested Time");
            lastIngestedCol.setCell(new AbstractCell<Date>() {
                @Override
                public void render(com.google.gwt.cell.client.Cell.Context context,
                        Date value, SafeHtmlBuilder sb) {
                    sb.appendHtmlConstant((DateTimeFormat.getFormat(CommonConstants.DATE_TIME_ISO8601).format(value)));
                }
            });
            lastIngestedCol.setSummaryType(new SummaryType<GranuleThroughputByProduct, Date, Long>() {
                @Override
                public Long calculate(
                        List<GranuleThroughputByProduct> m,
                        ValueProvider<GranuleThroughputByProduct, Date> valueProvider) {
                    Date latest = null;
                    for (GranuleThroughputByProduct t : m) {
                        if (latest == null || latest.before(t.getLastIngestedTime())) {
                            latest = t.getLastIngestedTime();
                        }
                    }
                    return latest.getTime();
                }
            });
            lastIngestedCol.setSummaryRenderer(new SummaryRenderer<GranuleThroughputByProduct>() {
                @Override
                public SafeHtml render(
                        Number value,
                        Map<ValueProvider<GranuleThroughputByProduct, ?>, Number> data) {
                    Date  latest = new Date(value.longValue());
                    return SafeHtmlUtils.fromTrustedString((DateTimeFormat.getFormat(CommonConstants.DATE_TIME_ISO8601).format(latest)));
                }
            });
            
            SummaryColumnConfig<GranuleThroughputByProduct, Date> lastObservedCol = new SummaryColumnConfig<GranuleThroughputByProduct, Date>(
                    props.lastObsTime(), 100, "Last Observed Time");
            lastObservedCol.setCell(lastIngestedCol.getCell());
            lastObservedCol.setSummaryType(new SummaryType<GranuleThroughputByProduct, Date, Long>() {
                @Override
                public Long calculate(
                        List<GranuleThroughputByProduct> m,
                        ValueProvider<GranuleThroughputByProduct, Date> valueProvider) {
                    Date latest = null;
                    for (GranuleThroughputByProduct t : m) {
                        if (latest == null || latest.before(t.getLastObsTime())) {
                            latest = t.getLastObsTime();
                        }
                    }
                    return latest.getTime();
                }
            });
            lastObservedCol.setSummaryRenderer(new SummaryRenderer<GranuleThroughputByProduct>() {
                @Override
                public SafeHtml render(
                        Number value,
                        Map<ValueProvider<GranuleThroughputByProduct, ?>, Number> data) {
                    Date  latest = new Date(value.longValue());
                    return SafeHtmlUtils.fromTrustedString((DateTimeFormat.getFormat(CommonConstants.DATE_TIME_ISO8601).format(latest)));
                }
            });
            
            SummaryColumnConfig<GranuleThroughputByProduct, Integer> thresholdCol = new SummaryColumnConfig<GranuleThroughputByProduct, Integer>(
                    props.threshold(), 40, "Threshold");
            thresholdCol.setCell(new AbstractCell<Integer>() {
                @Override
                public void render(com.google.gwt.cell.client.Cell.Context context,
                        Integer value, SafeHtmlBuilder sb) {
                    sb.appendHtmlConstant(value.intValue() + " min");
                }
            });
    
            // Create the list of all column configs
            List<ColumnConfig<GranuleThroughputByProduct, ?>> colList = new ArrayList<ColumnConfig<GranuleThroughputByProduct, ?>>();
            colList.add(productNameCol);
            colList.add(datasource);
            colList.add(backlogCol);
            colList.add(ingest1XCol);
            colList.add(ingest2XCol);
            colList.add(lastIngestedCol);
            colList.add(lastObservedCol);
            colList.add(thresholdCol);
    
            ColumnModel<GranuleThroughputByProduct> colModel = new ColumnModel<GranuleThroughputByProduct>(
                    colList);
            store = new ListStore<GranuleThroughputByProduct>(props.key());
            
            summary = new GroupSummaryView<GranuleThroughputByProduct>();
            summary.setForceFit(true);
            summary.setShowGroupedColumn(false);
    
            Grid<GranuleThroughputByProduct> grid = new Grid<GranuleThroughputByProduct>(
                    store, colModel);
            grid.setBorders(true);
            grid.setView(summary);
            grid.getView().setShowDirtyCells(false);
            grid.setHeight(400);
    
            Scheduler.get().scheduleFinally(new ScheduledCommand() {
                @Override
                public void execute() {
                    summary.groupBy(datasource);
                }
            });
    
            StringFilter<GranuleThroughputByProduct> shortNameFilter = new StringFilter<GranuleThroughputByProduct>(props.productShortName());
            DateFilter<GranuleThroughputByProduct> lastIngestedFilter = new DateFilter<GranuleThroughputByProduct>(props.lastIngestedTime());
            DateFilter<GranuleThroughputByProduct> lastObservedFilter = new DateFilter<GranuleThroughputByProduct>(props.lastObsTime());
    
            GridFilters<GranuleThroughputByProduct> filters = new GridFilters<GranuleThroughputByProduct>();
            filters.initPlugin(grid);
            filters.setLocal(true);
            filters.addFilter(shortNameFilter);
            filters.addFilter(lastIngestedFilter);
            filters.addFilter(lastObservedFilter);
            
            HtmlTemplate.MainPageLayout templates = GWT.create(HtmlTemplate.MainPageLayout.class);
            HtmlLayoutContainer htmlCon = new HtmlLayoutContainer(templates.getTemplate());
            HtmlLayoutContainer innerHtmlCon = new HtmlLayoutContainer("<div class=\"gridHolder\"></div>");
            innerHtmlCon.add(grid, new HtmlData(".gridHolder"));
            
            htmlCon.add(new Label("Ingest Backlog & Throughput"), new HtmlData(".pageHeader"));
            htmlCon.add(innerHtmlCon, new HtmlData(".pageContent"));
    
            return htmlCon;
        }
    
        public void setData(ArrayList<GranuleThroughputByProduct> data) {
            
            // Fill the list store with the data
            store.addAll(data);
            // Set the sort info so that it sorts by lastIngestedTime
            store.addSortInfo(new StoreSortInfo<GranuleThroughputByProduct>(props.lastIngestedTime(), SortDir.DESC));
            store.applySort(true);
            summary.collapseAllGroups();
            
        }
    
    }
    BacklogAndThroughputPresenter.java
    Code:
    package org.noaa.espc.nde.dhs.portal.mc.client.presenter;
    
    
    import java.util.ArrayList;
    
    import com.google.gwt.event.shared.HandlerManager;
    import com.google.gwt.user.client.Window;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.google.gwt.user.client.ui.HasWidgets;
    import com.google.gwt.user.client.ui.IsWidget;
    import com.google.gwt.user.client.ui.Widget;
    
    public class BacklogAndThroughputPresenter implements Presenter {
        public interface Display {
            
            void setData(ArrayList<GranuleThroughputByProduct> data);
            Widget asWidget();
            
        }
        
        private final FileMetadataServiceAsync metadataService;
        private final HandlerManager eventBus;
        private final Display display;
         
        public BacklogAndThroughputPresenter(FileMetadataServiceAsync metadataService,
                HandlerManager eventBus, Display display) {
            //super();
            this.metadataService = metadataService;
            this.eventBus = eventBus;
            this.display = display;
            bind();
            fetchGranuleCounts();
        }
    
        public void bind()
        {
            
            
        }
    
        private void fetchGranuleCounts() {
            
            metadataService.getGranuleThroughputByProduct(new AsyncCallback<ArrayList<GranuleThroughputByProduct>>(){
                public void onSuccess(ArrayList<GranuleThroughputByProduct> result) {
                    display.setData(result);
                }
    
                public void onFailure(Throwable caught) {
                    Window.alert("Error fetching product counts list");
                }
    
            });
        }
        
        @Override
        public void go(HasWidgets container, IsWidget widget) {
            
        }
    
    }
    Last edited by nstokoe; 30 Jan 2012 at 1:19 PM. Reason: updating code

  2. #2
    Sencha User rohdef's Avatar
    Join Date
    Mar 2010
    Location
    Aarhus, Denmark
    Posts
    67
    Vote Rating
    3
    rohdef is on a distinguished road

      0  

    Default


    I think the best approach is some kind of paging, where you for instance get 10 rows at the time and ask the service for the next 10 rows, when the user requests the next page.

    This will also make your application able to handle if the data you use expands.

    Unfortunately I haven't tried doing such things yet, but I think there's some of the other posts in the GXT 3 forums that might be helpful, and of course the examples

  3. #3
    Sencha User
    Join Date
    Oct 2011
    Posts
    20
    Vote Rating
    0
    nstokoe is on a distinguished road

      0  

    Default


    Thank you for the suggestion, but unfortunately paging on a Live GroupSummary Grid defeats the purpose of the grid, which is to have live updating summary rows for values sharing a common grouping parameter.

    With paging, you would never be able to get a summary of all your data, only summaries based on each pages' values, which for the purposes of my application are useless.

Thread Participants: 1

Tags for this Thread