4
Vote

Syscache and Deleting a Recordset Issue

description

I bring this form Discussions https://orchard.codeplex.com/discussions/451114

jao28 wrote:
I have upgraded to Orchard 1.7 and am using the Syscache module (nice module). It seems to be causing issues with the NogginBox.MailChimp module (another nice module). This question is not about the MailChimp module, it just serves as the example. In that module a recordset is deleted outside of the content manager. How can I tell Syscache to re-initiate the query rather than just take what is cached (as what is cached throws an error that the record is not there).

Here is the code that deletes using the repository:
_mergeVarsRepository.Delete(oldMergeVar);
I have tried "Flush" to reset the cache to no avail. It appears the cache issue is that the Part which is getting it's value from this repository does not know it has been changed (so SysCache is returning the part in it's last state). Because it is a Parent / Child hierarchy there is no way to use the Content Manager to accomplish the delete. Look forward to advice on the proper approach to managing:
  • Syscache
  • Deletion of a Child to cause cache reset
azltdanh wrote:
I have the same problem as jao28, I using this example from Orchard Document
public void UpdateRewardsForContentItem(
            ContentItem item,
            IEnumerable<RewardProgramEntry> rewards) {

            var record = item.As<RewardsPart>().Record;
            var oldRewards = _contentRewardRepository.Fetch(
                r => r.RewardsPartRecord == record);
            var lookupNew = rewards
                .Where(e => e.IsChecked)
                .Select(e => e.RewardProgram)
                .ToDictionary(r => r, r => false);
            // Delete the rewards that are no longer there
            // and mark the ones that should stay
            foreach(var contentRewardProgramsRecord in oldRewards) {
                if (lookupNew.ContainsKey(
                    contentRewardProgramsRecord.RewardProgramRecord)) {

                    lookupNew[contentRewardProgramsRecord.RewardProgramRecord]
                        = true;
                }
                else {
                    _contentRewardRepository.Delete(contentRewardProgramsRecord);
                }
            }
            // Add the new rewards
            foreach(var reward in lookupNew.Where(kvp => !kvp.Value)
                                           .Select(kvp => kvp.Key)) {
                _contentRewardRepository.Create(new ContentRewardProgramsRecord {
                    RewardsPartRecord = record,
                    RewardProgramRecord = reward
                });
            }
        }
Each time I update the Content, it will redirect to Index. But if I go to Edit action again, this error occur
An unhandled exception has occurred and the request was terminated. Please refresh the page. If the error persists, go back

No row with the given identifier exists[RealEstate.Models.PropertyAdvantagePartRecordContent#12237]

NHibernate.ObjectNotFoundException: No row with the given identifier exists[RealEstate.Models.PropertyAdvantagePartRecordContent#12237] at NHibernate.Impl.SessionFactoryImpl.DefaultEntityNotFoundDelegate.HandleEntityNotFound(String entityName, Object id) at NHibernate.Event.Default.DefaultLoadEventListener.Load(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) at Orchard.Data.Providers.AbstractDataServicesProvider.OrchardLoadEventListener.OnLoad(LoadEvent event, LoadType loadType) in c:\Orchard\src\Orchard\Data\Providers\AbstractDataServicesProvider.cs:line 130 at NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType) at NHibernate.Impl.SessionImpl.InternalLoad(String entityName, Object id, Boolean eager, Boolean isNullable) at NHibernate.Type.EntityType.ResolveIdentifier(Object id, ISessionImplementor session) at NHibernate.Collection.PersistentBag.InitializeFromCache(ICollectionPersister persister, Object disassembled, Object owner) at NHibernate.Cache.Entry.CollectionCacheEntry.Assemble(IPersistentCollection collection, ICollectionPersister persister, Object owner) at NHibernate.Event.Default.DefaultInitializeCollectionEventListener.InitializeCollectionFromCache(Object id, ICollectionPersister persister, IPersistentCollection collection, ISessionImplementor source) at NHibernate.Event.Default.DefaultInitializeCollectionEventListener.OnInitializeCollection(InitializeCollectionEvent event) at NHibernate.Impl.SessionImpl.InitializeCollection(IPersistentCollection collection, Boolean writing) at NHibernate.Collection.Generic.PersistentGenericBag`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at RealEstate.Services.PropertyService.BuildEditViewModel(PropertyPart p) at RealEstate.Controllers.PropertyAdminController.Edit(Int32 id, String returnUrl) at lambda_method(Closure , ControllerBase , Object[] ) at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass13.<InvokeActionMethodWithFilters>b__10() at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
After few minutes, I refresh Edit action, and it work again.

Anyone know the solution?

comments

sebastienros wrote Sep 24, 2013 at 8:12 PM

That's why I am also against having Syscache by default, there are some scenarios we can't handle, if it doesn't work in some custom cases then don't use it.

I will see how we can change the sample to fix it in this case.

sebastienros wrote Sep 24, 2013 at 8:13 PM

Also please try again with a recent ORchard version (1.7.x ?) because we have updated nhibernate.

brporter wrote Jan 20 at 7:34 PM

This remains an issue in 1.7.2, and is the same scenario that makes Orchard.Tags practically unusable on a SysCache enabled implementation. Not having a viable 2nd-level cache provider is a real challenge to performance and scale.

taramclean wrote Feb 21 at 4:05 PM

I had the same problem described so as a hack around I used the ISessionFactoryHolder to refresh the2nd level cache after an update.
_sessionFactoryHolder.GetSessionFactory().EvictCollection("[NAMESPACE].Record.Collection", Id);
_sessionFactoryHolder.GetSessionFactory().EvictEntity("[NAMESPACE].Record", Id);

jao28 wrote Feb 21 at 5:00 PM

taramclean, that is a very interesting tip. I think many developers would be more than happy to call a method to clear the cache. If that could be wrapped into a method called from OrchardServices interface (or something similar) then the developer could update the record and call the method. I realize it puts the responsibility on the developer to handle this and that older / existing modules won't just automatically have a fix, but even so, it is a step in the right direction.

Does this force your module to take an additional dependency?

taramclean wrote Feb 24 at 9:14 AM

Yes the additional dependency I needed was ISessionFactoryHolder ( Orchard.Data.SessionFactoryHolder )

In addition you can manually invalidate/refresh all of the 2nd level cache by doing this
SessionFactory.EvictQueries();
foreach (var collectionMetadata in this._SessionFactory.GetAllCollectionMetadata())
        SessionFactory.EvictCollection(collectionMetadata.Key);
foreach (var classMetadata in this._SessionFactory.GetAllClassMetadata())
        SessionFactory.EvictEntity(classMetadata.Key);
but this is a nuclear option. I would suggest just refreshing just the record that has been updated.