Premium support for our pure JavaScript UI components


Post by asd »

Hi Bryntum Team,

We have upgraded the scheduler from 3.1.9 to 4.1.6, with the latest release we are facing the browser becoming unresponsive for large dataset of event say around 3k.

We are using eventStore.add("EVENTS_ARRAY") to add events to the store.

When looked at the documentation further we see there are other methods "loadDataAsync" and "addAsync".

However with using "addAsync" function we see the same issue browser becoming unresponsive, but with using "loadDataAsync" browser is responsive but it always recreates the store instead of append.

Check the following code from bryntum example

import { DateHelper, StringHelper, Scheduler, Toast } from '../../build/scheduler.module.js?451402';
import shared from '../_shared/shared.module.js?451402';

const scheduler = new Scheduler({

appendTo          : 'container',
eventStyle        : 'border',
resourceImagePath : '../_shared/images/users/',

features : {
    stripe     : true,
    timeRanges : true,
    headerZoom : true,
    eventEdit  : {
        // Uncomment to make event editor readonly from the start
        // readOnly : true,
        // Add items to the event editor
        items : {
            // Using this ref hooks dynamic toggling of fields per eventType up
            eventTypeField : {
                type   : 'combo',
                name   : 'eventType',
                label  : 'Type',
                // Provided items start at 100, and go up in 100s, so insert after first one
                weight : 110,
                items  : ['Appointment', 'Internal', 'Meeting']
            },
            locationField : {
                type    : 'text',
                name    : 'location',
                label   : 'Location',
                weight  : 120,
                // This field is only displayed for meetings
                dataset : { eventType : 'Meeting' }
            },
            eventColorField : {
                type        : 'combo',
                label       : 'Color',
                name        : 'eventColor',
                editable    : false,
                weight      : 130,
                listItemTpl : item => StringHelper.xss`<div class="color-box b-sch-${item.value}"></div><div>${item.text}</div>`,
                items       : Scheduler.eventColors.map(color => [color, StringHelper.capitalize(color)])
            },
            linkField : {
                type     : 'displayfield',
                label    : 'Link',
                name     : 'id',
                weight   : 600,
                template : link => StringHelper.xss`<a href='//your.app/task/${link}' target='blank'>Open in new tab</a></div>`
            }
        }
    }
},

subGridConfigs : {
    locked : { width : 300 }
},

columns : [
    {
        type : 'resourceInfo',
        flex : 1,
        text : 'Staff'
    },
    {
        text   : 'Type',
        field  : 'role',
        flex   : 1,
        editor : {
            type        : 'combo',
            items       : ['Sales', 'Developer', 'Marketing', 'Product manager', 'CEO', 'CTO'],
            editable    : false,
            pickerWidth : 140
        }
    }
],

crudManager : {
    autoLoad  : true,
    transport : {
        load : {
            url : 'data/data.json'
        }
    },
    eventStore : {
        // Extra fields used on EventModels. Store tries to be smart about it and extracts these from the first
        // record it reads, but it is good practice to define them anyway to be certain they are included.
        fields : [
            'location',
            { name : 'eventType', defaultValue : 'Appointment' }
        ]
    }
},

barMargin  : 2,
rowHeight  : 50,
startDate  : new Date(2017, 1, 7, 8),
endDate    : new Date(2017, 1, 7, 22),
viewPreset : {
    base      : 'hourAndDay',
    tickWidth : 100
},

// Specialized body template with header and footer
eventBodyTemplate : data => StringHelper.xss`<i class="${data.iconCls || ''}"></i><section><div class="b-sch-event-header">${data.headerText}</div><div class="b-sch-event-footer">${data.footerText}</div></section>`,

eventRenderer({ eventRecord, resourceRecord, renderData }) {
    renderData.style = 'background-color:' + resourceRecord.color;

    return {
        headerText : DateHelper.format(eventRecord.startDate, 'LT'),
        footerText : eventRecord.name || '',
        iconCls    : eventRecord.iconCls
    };
},

listeners : {
    eventEditBeforeSetRecord({ source : editor, record : eventRecord }) {
        editor.title = `Edit ${eventRecord.eventType || ''}`;

        // Only CEO and CTO roles are allowed to play golf...
        if (eventRecord.name === 'Golf') {
            editor.widgetMap.resourceField.store.filter({
                filterBy : resource => resource.role.startsWith('C'),
                id       : 'golfFilter'
            });
        }
        else {
            // Clear our golf filter before editing starts
            editor.widgetMap.resourceField.store.removeFilter('golfFilter');
        }
    }
},

tbar : [
    {
        type    : 'checkbox',
        label   : 'Read only editor',
        tooltip : 'Toggle read only mode for the event editor',
        onChange({ checked }) {
            scheduler.features.eventEdit.readOnly = checked;
        }
    },
    {
        type     : 'button',
        icon     : 'b-icon b-icon-add',
        text     : 'Add new event',
        onAction : () => {
            const resource  = scheduler.resourceStore.first;

            if (!resource) {
                Toast.show('There is no resource available');
                return;
            }

            const event = new scheduler.eventStore.modelClass({
                resourceId   : resource.id,
                startDate    : scheduler.startDate,
                duration     : 1,
                durationUnit : 'h',
                name         : 'New task'
            });

            scheduler.editEvent(event);
        }
    }, {
        type     : 'button',
        icon     : 'b-icon b-icon-trash',
        color    : 'b-red',
        text     : 'Clear all events',
        onAction : () => scheduler.eventStore.removeAll()
    },
    '->',
    {
        type       : 'datefield',
        label      : 'View date',
        inputWidth : '8em',
        value      : new Date(2017, 1, 7),
        editable   : false,
        listeners  : {
            change : ({ value }) => scheduler.setTimeSpan(DateHelper.add(value, 8, 'hour'), DateHelper.add(value, 22, 'hour'))
        }
    }
]
});

setTimeout(() => {
	let eventARR = [];

for(let i=0;i<4000;i++){
   eventARR.push({
            "id"         : i,
            "resourceId" : "a",
            "name"       : "Team scrum " +i,
            "startDate"  : "2017-02-07 11:00",
            "endDate"    : "2017-02-07 13:00",
            "location"   : "Some office",
            "eventType"  : "Meeting",
            "eventColor" : "#ff0000",
            "iconCls"    : "b-fa b-fa-users"
        })
}
scheduler.eventStore.add(eventARR);
}, 1000)

Thanks,
Abhay


Post by asd »

We need to get the analysis ASAP, as this has been pushed to production and customers are facing the issue.


Post by mats »

Reproduced, thanks for providing clear test case. We'll look into it! https://github.com/bryntum/support/issues/3131


Post by asd »

The customers have upgraded this and there has been a escalations about the same.
Any ETA for the fix?


Post by mats »

No ETA, we will look at it next week!


Post by asd »

Any updates on the progress of the fix in 4.1.6. Please provide the ETA as several of our enterprise customers are impacted in production.


Post by Maxim Gorkovsky »

You can track issue status by tags. At the moment it has in progress which means we are currently working on it. When it ends up with resolved it means fix was merged to the release and will be included to the next nightly build.


Post by asd »

Hi Bryntum Team,

I see this being resolved, and available with 4.2.1.

When I was playing around with the examples, I did not see the unresponsive error for 4k events, but page was struck,
However I did see the error with 8k+ events, Also I see the events added are not smooth.

Also what is the correct method or function to add the events to eventStore is it "eventStore.add(eventARR);" or "eventStore. addAsync(eventARR);" or "eventStore.loadDataAsync(eventARR);"

Thanks,
Abhay


Post by Maxim Gorkovsky »

Hello.

Also what is the correct method or function to add the events to eventStore is it "eventStore.add(eventARR);" or "eventStore. addAsync(eventARR);" or "eventStore.loadDataAsync(eventARR);"

Please refer to this doc article showing difference between add and addAsync: https://bryntum.com/docs/gantt/#Scheduler/data/mixin/SharedEventStoreMixin#function-add

loadDataAsync replaces existing data in store. You can use this method to append the data too, if you concatenate exsting and new data, like:

const oldData = eventStore.toJSON();
const newData = getData() // assume this method returns array of data objects
eventStore.loadDataAsync([...oldData, ...newData]);

I did not see the unresponsive error for 4k events, but page was struck

Indeed, it takes about 8 seconds to load and process the data. Is this what you mean?
Previously store cache was rebuilt for every added record which was a major and obvious bottleneck. In the new patch release that problem is resolved leaving nothing obviously wrong. Profiler shows about 1s to process the data, about 3s for engine to recalculate the data and about 4s to refresh the view. Even though we only draw visible rows and events, it takes some time to calculate it. i.e. we process all rows and events to see which should be visible and which should not. Starting from version 4.0 we added auto assignment store and used thin version of the scheduling engine to process the data, which also slowed down things a bit.
We have a ticket to further improve event store performance here: https://github.com/bryntum/support/issues/3141

However I did see the error with 8k+ events, Also I see the events added are not smooth

You're right. I can notice it with 8k events. It happens because we add 8k event elements to the page. If you load same amount of events but divided between resources, it all works much faster. I opened a ticket to improve this: https://github.com/bryntum/support/issues/3146 Thank you for report!

Could you please help us to understand your use case? i.e. why do you need thousands of events loaded at once for only few resources? What does your application do?


Post by asd »

The use case is:

We have events around approx 30k, which are fetched in batch. Each batch has 6 requests the events fetched after the first batch is 9000 events, each request returns 1500 events.

So after the first batch is resolved we update the scheduler store with 9000 events response, till the time store updates with 9000 events the browser is becoming unresponsive and further batch request are failing.As mentioned in prev comment the processing is taking around 8 sec due to which the further request might be failing.

This was working with 3.1.9 version with the same load mentioned above.

The 9000 events are not assigned to 1 resource it is divided among approx 300 resources.

This is seen with latest 4.2.1 version also.


Post Reply