Hi Marten,
Where does aSharedConfig come from? If the result of getGlorpSession is a new Session, you`d have to registerAsOld: if you know the object is already in the database. Just a short hint: if a Session registers an object, it will always assune it is new, unless it already is in its caches at register time. A session does its bokkeeping by caching all registered objects, and an object is either registered when it is read from the db, or when you register it. Every session does keep its own cache(undoMap). Hth, Joachin [hidden email] schrieb: Hi, It looks like Glorp really doesn't like to commit a registered object which basically has not been updated.
10 timesRepeat: [self getGlorpSession inUnitOfWorkDo: [self getGlorpSession register: aSharedConfig "Do nothing really"]].
Whenever this happens it wants to create a new object with the same primary key. Why ? Regards, @+Maarten, --You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
Hi again,
Re: the same primary key. Is this consisting of attributes of the object, or do you use sequences? In the first case, this is logical, the latter case would need a bit of digging into your code.... [hidden email] schrieb: Hi, It looks like Glorp really doesn't like to commit a registered object which basically has not been updated.
10 timesRepeat: [self getGlorpSession inUnitOfWorkDo: [self getGlorpSession register: aSharedConfig "Do nothing really"]].
Whenever this happens it wants to create a new object with the same primary key. Why ? Regards, @+Maarten, --You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
In reply to this post by jtuchel
Hi Kartsen and Joachim, I use a unique Glorp session in my application of which getGlorpSession is the accessor method. The SharedConfig Object is very simple object that doesn't hold any links to other objects and the table will allways contain one single row with id 1. As it names said it holds some parameters that needs to be shared among other users like which day is a weekend day or how many hours a workday contains. In this particular case I am loading an MS Project file which agenda potentially modifies the SharedConfig object. The first time I load the file into my application all goes fine. If I load the file a second time shortly after it will hit the primary key error as nothing has been updated. Lets notice that I've experienced primary key errors at many many occasions but it allways remained unpredictable as it often concerned other linked Objects and seemed vaguely related to the cache policy. It is the same problem I talked about to Nial and Tom during the last ESUG, but until now I never managed to reproduce it on a simple case. Joachim you mention to use registerAsOld: honestly I never heard about that, are you meaning the private one ? privateRegisterAsOld: anObject "Register the object as something we already read from the database, skipping the isNew: test. Private! Normally you would just use register:" | realObject | currentUnitOfWork isNil ifTrue: [^self]. realObject := self realObjectFor: anObject ifNone: [^self].
currentUnitOfWork register: realObject
Obviously in this simple case I can easily work around the problem but I have hundreds of register: methods elsewhere in my application, so it would be good to know the intended policy here.
Regards,
Maarten,
classModelForSharedConfig: aClassModel aClassModel newAttributeNamed: #id. aClassModel newAttributeNamed: #current_version. "Float" aClassModel newAttributeNamed: #descriptor_version. "String" aClassModel newAttributeNamed: #organisation_update type: Integer. aClassModel newAttributeNamed: #hierarchy_update type: Integer. aClassModel newAttributeNamed: #relations_update type: Integer. aClassModel newAttributeNamed: #tablecreation type: Timestamp. aClassModel newAttributeNamed: #weekendsunday type: Boolean. aClassModel newAttributeNamed: #weekendmonday type: Boolean. aClassModel newAttributeNamed: #weekendtuesday type: Boolean. aClassModel newAttributeNamed: #weekendwednesday type: Boolean. aClassModel newAttributeNamed: #weekendthursday type: Boolean. aClassModel newAttributeNamed: #weekendfriday type: Boolean. aClassModel newAttributeNamed: #weekendsaturday type: Boolean. aClassModel newAttributeNamed: #secondsperday type: Integer
> "Joachim Tuchel" <[hidden email]> | Hi again, Hi, It looks like Glorp really doesn't like to commit a registered object which basically has not been updated. 10 timesRepeat: [self getGlorpSession inUnitOfWorkDo: [self getGlorpSession register: aSharedConfig "Do nothing really"]]. Whenever this happens it wants to create a new object with the same primary key. Why ? Regards, @+Maarten, You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
In reply to this post by jtuchel
To complement, I open a new instance of my application and inserted the following fragment of Code in my aplication when opening a file. 1 to: 1000 do: [:m | Transcript show:'Register aSharedConfig ',m printString;cr. self getGlorpSession inUnitOfWorkDo: [self getGlorpSession register: aSharedConfig]]. This time it runs fine, I repeat the operation 6, 7 times without any problems, meaning at 6000 succesfull registration of an unchanged Object after opening a session. Then I wait for maybe half an hour or maybe something more. I again open the file and run the above mentioned. This time it produces the key duplicate error on the first register: (m =1) with the enclosed errorLog. https://dl.dropboxusercontent.com/u/5848367/Error%20log ??? Personally I can only thing of some automatic cache behaviour which after a certain time consideres the Object to be a new Object. I don't know what else could come up with a different behaviour only after a certain time. Regards, Maarten, > [hidden email] | Hi Kartsen and Joachim, I use a unique Glorp session in my application of which getGlorpSession is the accessor method. The SharedConfig Object is very simple object that doesn't hold any links to other objects and the table will allways contain one single row with id 1. As it names said it holds some parameters that needs to be shared among other users like which day is a weekend day or how many hours a workday contains. In this particular case I am loading an MS Project file which agenda potentially modifies the SharedConfig object. The first time I load the file into my application all goes fine. If I load the file a second time shortly after it will hit the primary key error as nothing has been updated. Lets notice that I've experienced primary key errors at many many occasions but it allways remained unpredictable as it often concerned other linked Objects and seemed vaguely related to the cache policy. It is the same problem I talked about to Nial and Tom during the last ESUG, but until now I never managed to reproduce it on a simple case. Joachim you mention to use registerAsOld: honestly I never heard about that, are you meaning the private one ? privateRegisterAsOld: anObject "Register the object as something we already read from the database, skipping the isNew: test. Private! Normally you would just use register:" | realObject | currentUnitOfWork isNil ifTrue: [^self]. realObject := self realObjectFor: anObject ifNone: [^self]. currentUnitOfWork register: realObject Obviously in this simple case I can easily work around the problem but I have hundreds of register: methods elsewhere in my application, so it would be good to know the intended policy here. Regards, Maarten, classModelForSharedConfig: aClassModel aClassModel newAttributeNamed: #id. aClassModel newAttributeNamed: #current_version. "Float" aClassModel newAttributeNamed: #descriptor_version. "String" aClassModel newAttributeNamed: #organisation_update type: Integer. aClassModel newAttributeNamed: #hierarchy_update type: Integer. aClassModel newAttributeNamed: #relations_update type: Integer. aClassModel newAttributeNamed: #tablecreation type: Timestamp. aClassModel newAttributeNamed: #weekendsunday type: Boolean. aClassModel newAttributeNamed: #weekendmonday type: Boolean. aClassModel newAttributeNamed: #weekendtuesday type: Boolean. aClassModel newAttributeNamed: #weekendwednesday type: Boolean. aClassModel newAttributeNamed: #weekendthursday type: Boolean. aClassModel newAttributeNamed: #weekendfriday type: Boolean. aClassModel newAttributeNamed: #weekendsaturday type: Boolean. aClassModel newAttributeNamed: #secondsperday type: Integer > "Joachim Tuchel" <[hidden email]> | Hi again, Hi, It looks like Glorp really doesn't like to commit a registered object which basically has not been updated. 10 timesRepeat: [self getGlorpSession inUnitOfWorkDo: [self getGlorpSession register: aSharedConfig "Do nothing really"]]. Whenever this happens it wants to create a new object with the same primary key. Why ? Regards, @+Maarten, You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
In reply to this post by Maarten Mostert
HI Maarten,
I've never used inUnitOfWorkDo:, so I may be a bit off with my comments... First of all: yes, I was talking about what seems to be named privateRegisterAsOld: in current Glorp. In the last version that shipped with the previous VA Smalltalk, there was a method named registerAsOld: and it hat a few senders. So something has changed with this method. As Karsten already said, you need to see if the two "versions" of your sharedConfig are the same object (#hash / #identityHash). The way I read your business case description, I would guess that is the problem. You import the file and always create a new instance of your sharedConfig object, right? We are having a similar thing in our application: we import a list of banks and tax offices several times a year, and in our import code we always look for the existing object in the db and modify it and only create new instances and register them when there is no old one. This way we avoid duplicates. Another option would be a delete / insert combination. This is what I guess Karsten and I suspect is happening: Your session knows the old object and gets a new one registered. Since the new one is not == the old one, it registers it as a new object and on commit it generates an insert, which fails due to duplicate keys. Good luck, Joachim Am 30.01.14 23:45, schrieb [hidden email]:
-- ----------------------------------------------------------------------- Objektfabrik Joachim Tuchel [hidden email] Fliederweg 1 http://www.objektfabrik.de D-71640 Ludwigsburg http://joachimtuchel.wordpress.com Telefon: +49 7141 56 10 86 0 Fax: +49 7141 56 10 86 1-- You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
In reply to this post by jtuchel
Hi Tom, This is exactly what I do: It happened again this afternoon, and what I noticed is that the cache for the SharedConfig was empty when it happened ! The sharedConfig is the first object I read aftern logging onto my database as it notably containes the descriptor version and the application's version. And in reply to other remarks, yes aSharedConfig is unique respects hash and identity and is allways old. For the moment I conclude that if I have an existing Object which got emptied from the Cache Glorp will think it is new. This was not what I was expecting. For me register would deal with whatever new old updates etc.
This behaviour would however be in line with Joachims proposal to use privateRegisterAsOld: and confirms why I can register: 6000 times without problems while having an example in cache and get an exception when the cache is emptied, which is an essential difference. Something else goes in this direction. Over the last year I modifed my application to basically read only 2 Objects. The model's root Object which spans about 20 tables and the SharedConfiguration which spans just one table. For everything else I drill down the proxies. By respecting unique identity == througout my application, Glorp will allways find something existing within its cache and not consider things to be new objects, and a simple register: will do the job without hassle.
From my naif point of view however I should not need to use private methods and Glorp could/should use privateRegisterAsOld whenever it has exiting primary keys for an Object, and not bother me with primary key verifications.
For the moment I'll experiement with privateRegisterAsOld to see if that provides better consistancy. Regards,
@+Maarten,
> "Tom Robinson" <[hidden email]> | Maarten,
To update the single row in the database, I would do this:
When I looked at the walkback you posted to Dropbox, I noticed that the duplicate key error is coming from an attempt to INSERT. This means that Glorp thinks this is a new object, not an existing one. On 1/30/14, 5:45 PM, [hidden email] wrote:
-- You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
Maarten,
if you really do exactly update an object during import, and this object has been read by the very same session in which you change its values, then something is completely broken. I doubt this is a Glorp issue then and you really need to dig deep into what happens to find the cause. I remember strange things that I thought were bugs in Glorp and most often they were cause by myself and some stupid implementation of methods I should better not touch, like #= or #hash. You say your sharedConfig is not found in the cache. Did you check if an instance of your sharedConfig class is in the cache and possibly just not found due to #= or #== problems? Did you check that it was there after reading it? If you say you drill down proxies, do you actively do something there or do you just rely on Glorp's Proxy mechanism? I guess if you do something, try stopping that ;-) Your description sounds weirder and weirder to me... ;-) Joachim Am 31.01.14 22:01, schrieb [hidden email]:
-- You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
In reply to this post by Maarten Mostert
Maarten,
one more idea: if you update that object. Do you change its attributes that are mapped as primary keys? What types are they mapped as? Joachim Am 31.01.14 22:01, schrieb [hidden email]:
-- You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
In reply to this post by jtuchel
Dear Nial & Joachim,
> If you use #privateRegisterAsOld: to set the undoMap but do not populate
The above is exactly what I mean and experience and I am happy you now understand what I mean. A good example is also to send refresh to an Object. Of from there its easy to end up with a total mess as you have a model which has different proxy values for the same thing ! It'll drive you crazy. I tried for some time to refresh everything before sending an update, but that didn't help, as in many cases Glorp still managed to find identity differences far away from the object I actually tried to update and still trigger a primary key error.
> > If you populate the cache instead, you can just register the object in > the usual way as soon as you give the session a UnitOfWork. It will be > put in the undoMap and changes will be tracked; no need to for any > special call of #privateRegisterAsOld:. > > To populate the cache you must have the key as well as the value. You > can get this from the cache of the session in which you previously read > it or via the descriptor as in > session > cacheAt: > (session descriptorFor: anObject) > primaryKeyFor: anObject > put: anObject
Oké, a solution would be to write a registerExisting: method that veryfies wheter the Object is in the cache and if not puts it in there and doing ordinary registration afterwards. In this way perfect identity is maintained and we avoid to re-read the whole model or empty the cache by reseting the session. This can be a solution in some cases but would not be my preferred one. Normally Glorp seems to know when to update its cache based on its cache policy, and I think it should be that way. If I manage to maintain consistancy of identity within my image I should only need to tweak my cache policy to balance between minimised database traffic and raised out of sync problems.
Understanding how Glorp does its magic in refreshing its cache can help to make it failsafe. Currently I suspect Glorp to know how to update its cache up to a point where it manages to find perfect identity, and to be lost the moment its requested a value which is not connected with the rest of the model and no longer in cache, which is the case with my SharedConfig object.
The downside of my single object knows all other approach is that in order to keep perfect identity I'll need to request all the airplane tickets of a client to find the one who left from London, which is not very efficient.
> > - you tell a fresh Glorp session to trust you and treat the object
I only work with one session, resetting it is the last resort, however I would love to know how to tell Glorp to mark something as Dirty so it triggers a refresh without disturbing the rest of the model.
Good to know will keep it in mind.
Thanks,
@+Maarten, -- To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
Hi everyone, I know experiment with the following registerExisting: method which walks nicely over my initial problem. I have no idea yet however if I can reinsert an object someone else deleted from the database while keeping perfect identity in the image. If it proofs to work however it would be an elegant way. Regards, @+Maarten,
registerExisting: anObject "Register an existing object which complies to full identity. First Check wheter the object exist on the database by counting, if it no longer exist set its primary key to nil and go directly to register. If a single instance exist on the database check wheter its in cache, if not adds it to the cache to avoids any primarykey error. For the moment this method is limited to object with a single primary key but that can envolve easily" | aCounter keyName | (self descriptorFor: anObject) primaryTable primaryKeyFields size > 1 ifTrue: [self error: 'This object has multiple primary keys']. aCounter := self count: anObject class where: [:each | keyName := (self descriptorFor: anObject) primaryTable primaryKeyFields first name. (each perform: keyName asSymbol) = ((self descriptorFor: anObject) primaryKeyFor: anObject)]. aCounter = 1 ifTrue: [super halt. ((self cacheFor: anObject) includesKey: ((self descriptorFor: anObject) primaryKeyFor: anObject) withClass: anObject class) ifFalse: [MyDialog warn: 'Not in cache'. (self cacheFor: anObject) at: ((self descriptorFor: anObject) primaryKeyFor: anObject) ifAbsentPut: [anObject]]] ifFalse: [anObject perform: (keyName , ':') asSymbol with: nil]. ^self register: anObject
> [hidden email] |
Dear Nial & Joachim,
> If you use #privateRegisterAsOld: to set the undoMap but do not populate
The above is exactly what I mean and experience and I am happy you now understand what I mean. A good example is also to send refresh to an Object. Of from there its easy to end up with a total mess as you have a model which has different proxy values for the same thing ! It'll drive you crazy. I tried for some time to refresh everything before sending an update, but that didn't help, as in many cases Glorp still managed to find identity differences far away from the object I actually tried to update and still trigger a primary key error.
> > If you populate the cache instead, you can just register the object in > the usual way as soon as you give the session a UnitOfWork. It will be > put in the undoMap and changes will be tracked; no need to for any > special call of #privateRegisterAsOld:. > > To populate the cache you must have the key as well as the value. You > can get this from the cache of the session in which you previously read > it or via the descriptor as in > session > cacheAt: > (session descriptorFor: anObject) > primaryKeyFor: anObject > put: anObject
Oké, a solution would be to write a registerExisting: method that veryfies wheter the Object is in the cache and if not puts it in there and doing ordinary registration afterwards. In this way perfect identity is maintained and we avoid to re-read the whole model or empty the cache by reseting the session. This can be a solution in some cases but would not be my preferred one. Normally Glorp seems to know when to update its cache based on its cache policy, and I think it should be that way. If I manage to maintain consistancy of identity within my image I should only need to tweak my cache policy to balance between minimised database traffic and raised out of sync problems.
Understanding how Glorp does its magic in refreshing its cache can help to make it failsafe. Currently I suspect Glorp to know how to update its cache up to a point where it manages to find perfect identity, and to be lost the moment its requested a value which is not connected with the rest of the model and no longer in cache, which is the case with my SharedConfig object.
The downside of my single object knows all other approach is that in order to keep perfect identity I'll need to request all the airplane tickets of a client to find the one who left from London, which is not very efficient.
> > - you tell a fresh Glorp session to trust you and treat the object
I only work with one session, resetting it is the last resort, however I would love to know how to tell Glorp to mark something as Dirty so it triggers a refresh without disturbing the rest of the model.
Good to know will keep it in mind.
Thanks,
@+Maarten,
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
Free forum by Nabble | Edit this page |