Question on GAEContainer with cache enabled

Hello Vaadin Experts,

I am using Johan Selänniemi’s
Vaadin GAEContainer addon
(0.6.1) for my application. The GAEContainer is connected to a Vaadin Table object as its ContainerDataSource. If I do not use any cache then everything works fine but when I set caching then I get the following issues (see the attached screenshots) :

1.:
when I add the second,third,etc. new item into an empty container it is displayed wrong in the table: the property values of the first item are displayed where the new item property values should be displayed. When I change the sorting by clicking on any column header then the items will be listed correctly, but when I click the same sort of type when the item was added then the table displays the items wrong again.
The entities are strored correctly in the GAE datastore. When I restart the application and then add new items as third,4th,etc. then its working ok again, thus I think problem exist only at the point where the cache contains only 0 or 1 item already before adding a new item.

2.:
another issue when I delete an item then a small empty table row is displayed.

I suppose the issues are related to the chaching because without using any cache my app works great and the item add / delete is always executed in the datastore correctly, I checked it with the GAE datastore viewer. No filtering nor sorting are added to my table.

Could you give me please some hints at which points I should debug the code execution? I already started debugging the CachingProviderImpl.class.


static final LocalMemoryCacheConfig localMemoryCacheConfig = new
                    LocalMemoryCacheConfig.Builder()
                    .withCacheFilteredIndexes(true)
                    .withCacheFilteredSizes(true)
                    .withIndexCapacity(50)
                    .withIndexLifeTime(600)
                    .withItemCapacity(1000)
                    .withItemLifeTime(600)
                    .withLineSize(50)
                    .withRemoveStrategy(LocalMemoryCacheConfig.RemoveStrategy.LRU)
                    .withSizeCapacity(20)
                    .withSizeLifeTime(600)
                    .Build();
       
       Cache localMemory = CacheFactory.getCache(localMemoryCacheConfig);        
       Cache memCache = CacheFactory.getDefaultMemCache();

// -- Override getItemIds() which throws UnsupportedException by default
       private class MyGAEContainer extends GAEContainer {
             
             static final long serialVersionUID = 1L;
             
             public MyGAEContainer(String kind) {
                    // Create a container with two caches, optimistic locking and manual commits:
                    super(kind, false, true, localMemory, memCache);
                    //super(kind);
             }
             
             @Override
             public Collection<Long> getItemIds() {

                    ArrayList<Long> itemIds = new ArrayList<Long>();
                    for( int i=0; i<size(); i++) {
                           itemIds.add((Long)getIdByIndex(i));
                    }
                    return Collections.unmodifiableCollection(itemIds);                      
             }
             
       };

             gaeContainerInsurance = new MyGAEContainer("Insurance");
             gaeContainerInsurance.addContainerProperty("zip", String.class,  "");
             gaeContainerInsurance.addContainerProperty("town", String.class,  "");
             gaeContainerInsurance.addContainerProperty("address2", String.class,  "");
             gaeContainerInsurance.addContainerProperty("address1", String.class,  "");             
             gaeContainerInsurance.addContainerProperty("companyname", String.class,  "");

             this.table_insurance.setContainerDataSource(gaeContainerInsurance);
             this.table_insurance.setWriteThrough(false);
             this.table_insurance.setVisibleColumns(new String[] {"companyname","address1","address2","town","zip"});
             this.table_insurance.setColumnHeader("companyname", "Company Name");
             this.table_insurance.setColumnHeader("address1", "Address 1");
             this.table_insurance.setColumnHeader("address2", "Address 2");
             this.table_insurance.setColumnHeader("town", "Town");
             this.table_insurance.setColumnHeader("zip", "Zip"); 
             this.table_insurance.setSelectable(true); 
             this.table_insurance.setImmediate(true);
             this.table_insurance.setEditable(false); 

...

itemId = gaeContainerInsurance.addItem(); // new
VersionedGAEItem item = (VersionedGAEItem)gaeContainerInsurance.getItem(itemId);                                                       

item.getItemProperty("companyname").setValue(textField_insurancecompany_name.getValue());
item.getItemProperty("address1").setValue(textField_insurancecompany_addr1.getValue());
item.commit();
refreshTableInsurance();
table_insurance.select(itemId);              
...

private void refreshTableInsurance() {
                               
                               /*
                                 setContainerDataSource calls:
                                                              setVisibleColumns(col.toArray());
                               // Assure visual refresh
                               resetPageBuffer();
                               enableContentRefreshing(true);
                               
                                               setVisibleColumns calls:               refreshRowCache();
                               */
                               this.table_insurance.setContainerDataSource(this.table_insurance.getContainerDataSource());
                               this.table_insurance.setVisibleColumns(new String[] {"companyname","address1","address2","town","zip"});                          
}

...

Object itemId = table_insurance.getValue();
if( null!=itemId ) {
                gaeContainerInsurance.removeItem(itemId);
                refreshTableInsurance();                                                                                          
}

12787.jpg
12788.jpg
12789.jpg
12790.jpg
12791.txt (4.59 KB)

It seems to me that when LocalMemoryCacheImpl.getIndexes called after an item deleted from the container it still gives back the itemId of the deleted item, that is why an empty small row displayed in the table on my screen.

I found the following comment in the source code of the CachingProviderImpl class:

[i]

  • Dataprovider implementation that supports caching and queries.
  • This implementation does not clear indexes when entities are added, deleted or changed. Manage data consistency by specifying lifetime.
    *Raw data in caches (ie entities) are updated on delete and add.

[/i]

I suppose I should refresh / recreate / update the index cache somehow after each entity add / delete if want to use index cache at all.