Hi, That clears up some things, and I understand how the 'cache' works now, thanks!
I did a little bit of research on the lifecycle of a Seaside session, but wasn't very successful. Largely because there is a lot of different/outdated documentation around, including class comments in Pharo, which has caused me to get lost 'in the woods'. I haven't figured out exactly how the session expiration actually operates. The class comment of WASession mentions #defaultTimeoutSeconds or #expire, but both of these seem to be absent from Seaside. The WAExpirySession class only keeps some counters. Can you perhaps point me in the right direction? For example, which object manages the Seaside sessions and where can I find (and influence) the expiration behavior? How can I be sure that our WASession is properly cleaned up so it won't prevent domain model objects from being 'released' from the weak dictionary that is used by the Voyage 'cache'? Kind regards, Jonathan van Alteren Founding Member | Object Guild jvalte...@objectguild.com On 10 Oct 2019, 12:55 +0200, Norbert Hartl <norb...@hartl.name>, wrote: > Hi, > > > Am 10.10.2019 um 11:48 schrieb Jonathan van Alteren > <jvalte...@objectguild.com>: > > > Hi Norbert, > > > > Thank you very much for your extensive answer. > > > > For starters, we would be happy to have an option available for the course > > grained handling that you mention. I'd be interested to hear if there are > > any options besides GemStone available to be used with Voyage/MongoDB. > > I think Gematone has a model that serves these use cases really well. In > Pharo there is not much that makes your life easy. But most of this problems > can be solved sufficiently kust not generically. Every use case has its > constraints and you can model a solution for it. The main problem in voyage > stays that the cache is per repository. We need to think along that line if a > session or request cannot get its private copy of the database object. > > > Your mention of the Mongo cache being a weak dictionary is very > > interesting, I did not know that (it doesn't seem to be documented). Yes, > > the Seaside sessions seem to be the path through which these objects remain > > referenced. When I inspect all instances of our session class, I see old > > session objects hanging around that still reference our root application > > component with the rendering tree containing various references to our > > domain model objects. > > You asked Esteban about the cache. I answer that here. Everybody stumbles > over the name cache and Esteban is reluctant to give it another name 😛 > The cache is only for establishing identity in the image. The basic thing > with objects in an external system is that its identity (location in the > heap) becomes an identity parameter like an id. So when you have externalized > objects and you load the object with the same id twice in the image you will > make sure that there will only be one object for it and the object from the > first query and from the second query are identical meaning comparing both > with == returns true. So the cache keeps the database reference and the > business object in a weak dictionary. If the object with the same id is > queried from the database it returns the object it already has. If the object > is not referenced anymore the entry will be removed from the cache. It is > nothing more than this. > > > > Can you tell me why the session objects don't get garbage collected? > > Should we manually clean up our session object somehow? > > > I‘m not sure what you mean. If the session is not referenced than it just > means the garbage collector did not remove it. But this won‘t keep the cache > from removing the entry. But seaside sessions have an expiry time. So there > is a duration while seaside keeps referencing the session and therefor all > objects that are kept in the session are still connecte keeping the objects > in the voyage cache. > > > I must admit I'm a bit out of my comfort zone here, after working in Java > > for close to 20 years ;-) We explicitly don't want to use any relational > > databases for our own application (perhaps only for integrating with > > customer data). I still haven't fully integrated the conceptual differences > > in my mental model, about how to work with objects in an image based/object > > persistence environment. > > > > I did look into using a Voyage repository filter, which is mentioned (or > > buried deep I should say ;-)) in this post: > > https://pharoweekly.wordpress.com/2017/02/20/consortium-action-13-17-feb/. > > What if we were to use a dynamic variable like this for each session > > instance? Then at least each user will have her/his own cache. But that > > doesn't answer the need for a kind of rollback mechanism within the same > > Voyage cache/session. And then there is your comment about potentially > > having multiple copies of an identical object... > > As I told in my last mail. The session or request based cache has also > problems because if you attach an object you queried while in the > session/request to somewhere more global you introduce subtle bugs. Maybe we > need a two level cache, one that knows all objects referenced in the image > and one that keeps the private copies based on the image based cache. This > way we could know that multiple sessions/request are keeping a copy of the > same object and we could determine merge conflicts. Need to think more about > this > > > > I wonder what patterns other Seaside 'enterprise' application developers > > are using. > > > > > You are right, the term object transactionality doesn't make much sense ;-) > > We are not using Magritte (and probably won't). I don't know much about > > Magritte, but it feels like it might be incompatible with the behavioral, > > 'pure' object approach that we want to use. However, I am interested to > > investigate. Can you recommend any good documentation sources for learning > > Magritte from an application architecture perspective? > > > Magritte is a meta description of your objects. It is like having powerful > annotation objects. These can be used to create different views on your > model. There is the PhD paper of Lukas Renggli on the net and maybe something > in the pharo for the enterprise book. > > > Also, I'm interested to hear more about the modification tracking approach > > you are working on. Please drop me a personal note if you are willing to > > collaborate on this. > > > I‘m happy to pick up that stuff. It is just that as always there is not > enough time to do it. But at least I can (yet again) try to rip it out from > out > r product and release something > > Norbert > > > > > Kind regards, > > > > Jonathan van Alteren > > > > Founding Member | Object Guild > > jvalte...@objectguild.com > > On 8 Oct 2019, 12:54 +0200, Norbert Hartl <norb...@hartl.name>, wrote: > > > Hi, > > > > > > > Am 08.10.2019 um 12:05 schrieb Jonathan van Alteren > > > > <jvalte...@objectguild.com>: > > > > > > > > Hello all, > > > > > > > > We are having some issues with using Voyage/Mongo for a customer > > > > project that I'd like to get your feedback on. > > > > > > > > The customer application is a form based business web application using > > > > Seaside with object persistence using Voyage with MongoDB on Pharo 7.0. > > > > The application is deployed on a dedicated Linux production server > > > > running MongoDB version 4.2. > > > > > > > > The application is used to manage meeting agendas and minutes. After > > > > opening the agenda view of a future meeting, the user can add an item > > > > to the agenda by clicking a button. This calls an item editor component > > > > which answers when a Save or Cancel button is clicked. The agenda view > > > > component itself also has a Save button, which performs a Voyage save > > > > of the object aggregate (agenda + items). > > > > > > > > We've encountered an issue where a user makes changes to an agenda, but > > > > does not click the Save button. Instead, the user closes the browser or > > > > uses the navigation to go to a different part of the application. When > > > > navigating back to the original agenda, the changes made previously > > > > (e.g. items added) are still being displayed, even though they were > > > > never explicitly saved. > > > > > > > > It does not matter if we select the agenda aggregate object instance > > > > using Voyage or access it in a different way. Changes to the state of > > > > the object are retained, even though a Voyage save was never sent to > > > > the agenda instance. The cause seems to be that the Voyage repository > > > > caches the object instance and thus on select, it returns an object > > > > that is in a different state than how it was persisted. > > > > > > > > This all seems to come down to object transactionality. > > > > > > > > We have a need to 'cancel' changes made to an object instance. Before > > > > working with Pharo/Smalltalk in a non-image based environment, I was > > > > used to do this by retrieving the original object from persistence > > > > again. This also allowed for a convenient way to detect changes to an > > > > object's state, which we are missing at the moment too. > > > > > > > > We know that moving to GemStone can help us with these issues, but our > > > > current planning of customer projects does not allow us to do this > > > > within the next 3 months. And we really need to find a way to tackle > > > > these issues . > > > > > > > > > > > > Your feedback is greatly appreciated! > > > > > > > this is none to be confusing to a lot of people. If you map a memory > > > object graph to a database there are no intrinsic points of transactions > > > unless you put them into your application model. Gemstone or any > > > transaction based handling won‘t help you if your use case is not as > > > coarse grained as discarding all modified data and not just some. Or you > > > need to hop into nested transactions which are supported by Gemstone IMHO > > > but there is rules to care about, too, if you want to use them. > > > > > > Regarding your application you modify data and this is kept. The cache in > > > mongo is a weak dictionary. This means that if you get the object with > > > the changes a second time it means these objects are referenced > > > somewhere. Seaside sessions seems to be the obvious thing here. If you > > > have a reachable object (e.g. server singleton -> handler -> seaside -> > > > seaside session -> business object) then mongo keeps the object in its > > > cache because it needs to be able to establish identity on further > > > queries. Ein emoving all the changes from an object you keep in memory > > > wouldn‘t be really good if the persistence layer would do it. > > > > > > The problem is obvious but the solution is not. We could make the cache > > > session or request based which would circumvent the problem. But if an > > > object would be attached to something outside the session/request which > > > you cannot forbid it causes real problems like multiple copies to an > > > identical object. > > > > > > I don‘t know what you mean with object transactionality because it makes > > > no sense in my head. But you have basically two approaches to solve the > > > problem. If you would use magritte you would have it at hand. There each > > > UI component gets just a memento of the business object for modification. > > > You need to commit the UI component in order to modify the business > > > object. So you can commit on user interaction and separately on > > > persistence which allows a huge set of use cases. > > > The other one I‘m working on since a while is a proper modification > > > tracking. In this you change the objects directly but have a registry > > > with the dirty objects which you can act upon. I have a sample > > > modification tracker that uses readOnly object write barrier for objects. > > > On write attempt it registers the object and the write operation as > > > command pattern. You can undo modifications of a single object where all > > > commmands are undone on that object. Or you abort all modifications at > > > once. It is basically working but has the problems on concurrent access. > > > As the mongo cache is image wide two concurrent requests interfere with > > > each other. > > > So you can see that the problem is not that easy to solve. > > > > > > TL;DR a memento approach might your best and cleanest option right now. > > > > > > Norbert > > > > > > > > Kind regards, > > > > > > > > > > > > Jonathan van Alteren > > > > > > > > Founding Member | Object Guild > > > > jvalte...@objectguild.com > > > > > > > > > >