Transcient instance variable

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Transcient instance variable

Gemstone/S mailing list
I'm considering some refactoring that would benefit from having access to transcient instance variables.
Something like SessionTemps, but stored on an instance variable and cleared on each abort or commit.
It would be used to store pending updates. We do that now with a wrapper object, which works fine, 
but the code would be cleaner if the domain object wrapped the pending updates instead.

Anything like that available?

Thanks for any suggestions,
Bob Nemec
HTS

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Transcient instance variable

Gemstone/S mailing list

Re-send to list...

 

From: Paul Baumann
Sent: Monday, November 10, 2014 10:47
...
Subject: RE: [GemStone-Smalltalk] Transcient instance variable

 

Hi Bob,

 

"System rcValueCache" is flushed  with a view/transaction change (commitTransaction, abortTransaction, beginTransaction, continueTransaction). Here is an example that shows view-transient behavior:

 

| anObject get0 get1 |

System beginTransaction.

anObject := Object new.

System rcValueCacheAt: #plbTest put: #remembered for: anObject.

get0 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.

System commitTransaction.

get1 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.

Array with: get0 with: get1

 

anArray( #'remembered', nil)

 

Your domain object can use itself to retrieve by-reference attributes. A view-transient attribute would be like this:

 

viewTransient_plbTest

                ^System rcValueCacheAt: #plbTest for: self otherwise: nil

 

viewTransient_plbTest: anObject

                System rcValueCacheAt: #plbTest put: anObject for: self

 

A session-transient attribute would be like this:

 

sessionTransient_plbTest

                |dict |

                ^(dict := SessionTemps current at: #plbTest otherwise: nil) notNil

                                ifTrue: [dict at: self otherwise: nil].

 

sessionTransient_plbTest: anObject

                (SessionTemps current at: #plbTest ifAbsentPut: [IdentityDictionary new])

                                at: self put: anObject.

 

A wrapper object held in an object attribute can be used for better performance when needed. Your domain object would have an attribute that holds information that can find cached data quicker. If the attribute is nil then the no lookup is necessary (saving a search). If the attribute is not nil then it can be something like a reserved index position within a known location (not strongly referenced) that would contain the data. Years ago I had prototyped a cache framework that was able to use a special wrapper for weak references to related objects. It can be done--if you really need that level of performance.

 

Paul Baumann

 

 

 

From: GemStone-Smalltalk [mailto:[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 08:25
To: [hidden email]
Subject: [GemStone-Smalltalk] Transcient instance variable

 

I'm considering some refactoring that would benefit from having access to transcient instance variables.

Something like SessionTemps, but stored on an instance variable and cleared on each abort or commit.

It would be used to store pending updates. We do that now with a wrapper object, which works fine, 

but the code would be cleaner if the domain object wrapped the pending updates instead.

 

Anything like that available?

 

Thanks for any suggestions,

Bob Nemec

HTS



This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Transcient instance variable

Gemstone/S mailing list
Paul,
Thanks for the note on 'System rcValueCache'. I can see use using it for other cases.

For the 'data wrapper' code change I'm considering performance is key. The check for updated values would be done on each in 'get' method, so anything that slows that down would be noticeable. 

I was thinking of code like... (we're still brainstorming this)

foo
    ^self getUpdatedValueAt: #foo with: foo

getUpdatedValueAt: aKey with: aValue
    self dataWrapper isNil ifTrue: [^aValue].
    ^self dataWrapper getUpdatedValueAt: aKey with: aValue  "where aValue is answered if there are no updates for2 #foo"


...we may just do this and take responsibility for clearing the data wrapper instVar just prior to a commit.

Bob




On Monday, November 10, 2014 12:55 PM, Paul Baumann via GemStone-Smalltalk <[hidden email]> wrote:


Re-send to list...
 
From: Paul Baumann
Sent: Monday, November 10, 2014 10:47
...
Subject: RE: [GemStone-Smalltalk] Transcient instance variable
 
Hi Bob,
 
"System rcValueCache" is flushed  with a view/transaction change (commitTransaction, abortTransaction, beginTransaction, continueTransaction). Here is an example that shows view-transient behavior:
 
| anObject get0 get1 |
System beginTransaction.
anObject := Object new.
System rcValueCacheAt: #plbTest put: #remembered for: anObject.
get0 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.
System commitTransaction.
get1 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.
Array with: get0 with: get1
 
anArray( #'remembered', nil)
 
Your domain object can use itself to retrieve by-reference attributes. A view-transient attribute would be like this:
 
viewTransient_plbTest
                ^System rcValueCacheAt: #plbTest for: self otherwise: nil
 
viewTransient_plbTest: anObject
                System rcValueCacheAt: #plbTest put: anObject for: self
 
A session-transient attribute would be like this:
 
sessionTransient_plbTest
                |dict |
                ^(dict := SessionTemps current at: #plbTest otherwise: nil) notNil
                                ifTrue: [dict at: self otherwise: nil].
 
sessionTransient_plbTest: anObject
                (SessionTemps current at: #plbTest ifAbsentPut: [IdentityDictionary new])
                                at: self put: anObject.
 
A wrapper object held in an object attribute can be used for better performance when needed. Your domain object would have an attribute that holds information that can find cached data quicker. If the attribute is nil then the no lookup is necessary (saving a search). If the attribute is not nil then it can be something like a reserved index position within a known location (not strongly referenced) that would contain the data. Years ago I had prototyped a cache framework that was able to use a special wrapper for weak references to related objects. It can be done--if you really need that level of performance.
 
Paul Baumann
 
 
 
From: GemStone-Smalltalk [mailto:[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 08:25
To: [hidden email]
Subject: [GemStone-Smalltalk] Transcient instance variable
 
I'm considering some refactoring that would benefit from having access to transcient instance variables.
Something like SessionTemps, but stored on an instance variable and cleared on each abort or commit.
It would be used to store pending updates. We do that now with a wrapper object, which works fine, 
but the code would be cleaner if the domain object wrapped the pending updates instead.
 
Anything like that available?
 
Thanks for any suggestions,
Bob Nemec
HTS


This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk



_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Transcient instance variable

Richard Sargent
Administrator
In reply to this post by Gemstone/S mailing list
Gemstone/S mailing list wrote
I'm considering some refactoring that would benefit from having access to transcient instance variables.
Something like SessionTemps, but stored on an instance variable and cleared on each abort or commit.
It would be used to store pending updates. We do that now with a wrapper object, which works fine,
but the code would be cleaner if the domain object wrapped the pending updates instead.
Hi Bob,

You could use DbTransient instances in the instance variable. They might do what you need.
Refer to the Programming Guide. The 3.1 guide has details in section 3.3, Creating Classes with Special Cases of Persistence.
Reply | Threaded
Open this post in threaded view
|

Re: Transcient instance variable

Gemstone/S mailing list
In reply to this post by Gemstone/S mailing list

Hi Bob,

 

It seems like you are currently thinking of using the dataWrapper to note the new change. Consider if it will suit your needs better to use your dataWrapper to note the values before the first inst var change (like with a shallow copy), and allow the change to be made to the inst vars. The shallowCopy can be registered in some manager and the dataWrapper inst var of the copy can refer to the original object.

 

assignDataWrapper

                dataWrapper isNil ifTrue: [

                                dataWrapper := self copy.

                                copy dataWrapper: self.

                                (SessionTemps current at: #ChangeManager ifAbsentPut: [Array new])

                                                add: dataWrapper.

                ].

                ^dataWrapper

 

foo: newValue

                self assignDataWrapper.

                foo := newValue

 

foo

                ^foo

 

This approach performs better because changes to objects are less frequent than access to variables. The cost of making a change is the creation of a copy and adding it to some managed collection. The code needs to make consistent use of the custom setter methods, or else you'd try to hook some generic object dirtying code.

 

The ChangeManager can be defined however you want changes to survive. You can detect changes both at the object level and by the manager. You'd probably either do transaction changes through your ChangeManager or notify the manager afterwards (note that #continueTransaction has a notification bug). If your dataWrapper/copy is a committed object then you'd know that an unmanaged commit happened (the inst vars may now also contain changes from another session). If ChangeManager has copies that differ from what the originals noted for your session then you might have had an unmanaged begin/abort/continue transaction.

 

Regards,

 

Paul Baumann

 

 

From: GemStone-Smalltalk [mailto:[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 16:52
To: [hidden email]
Subject: Re: [GemStone-Smalltalk] Transcient instance variable

 

Paul,

Thanks for the note on 'System rcValueCache'. I can see use using it for other cases.

 

For the 'data wrapper' code change I'm considering performance is key. The check for updated values would be done on each in 'get' method, so anything that slows that down would be noticeable. 

 

I was thinking of code like... (we're still brainstorming this)

 

foo

    ^self getUpdatedValueAt: #foo with: foo

 

getUpdatedValueAt: aKey with: aValue

    self dataWrapper isNil ifTrue: [^aValue].

    ^self dataWrapper getUpdatedValueAt: aKey with: aValue  "where aValue is answered if there are no updates for2 #foo"

 

 

...we may just do this and take responsibility for clearing the data wrapper instVar just prior to a commit.

 

Bob

 

 

 

On Monday, November 10, 2014 12:55 PM, Paul Baumann via GemStone-Smalltalk <[hidden email]> wrote:

 

Re-send to list...

 

From: Paul Baumann
Sent: Monday, November 10, 2014 10:47
...
Subject: RE: [GemStone-Smalltalk] Transcient instance variable

 

Hi Bob,

 

"System rcValueCache" is flushed  with a view/transaction change (commitTransaction, abortTransaction, beginTransaction, continueTransaction). Here is an example that shows view-transient behavior:

 

| anObject get0 get1 |

System beginTransaction.

anObject := Object new.

System rcValueCacheAt: #plbTest put: #remembered for: anObject.

get0 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.

System commitTransaction.

get1 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.

Array with: get0 with: get1

 

anArray( #'remembered', nil)

 

Your domain object can use itself to retrieve by-reference attributes. A view-transient attribute would be like this:

 

viewTransient_plbTest

                ^System rcValueCacheAt: #plbTest for: self otherwise: nil

 

viewTransient_plbTest: anObject

                System rcValueCacheAt: #plbTest put: anObject for: self

 

A session-transient attribute would be like this:

 

sessionTransient_plbTest

                |dict |

                ^(dict := SessionTemps current at: #plbTest otherwise: nil) notNil

                                ifTrue: [dict at: self otherwise: nil].

 

sessionTransient_plbTest: anObject

                (SessionTemps current at: #plbTest ifAbsentPut: [IdentityDictionary new])

                                at: self put: anObject.

 

A wrapper object held in an object attribute can be used for better performance when needed. Your domain object would have an attribute that holds information that can find cached data quicker. If the attribute is nil then the no lookup is necessary (saving a search). If the attribute is not nil then it can be something like a reserved index position within a known location (not strongly referenced) that would contain the data. Years ago I had prototyped a cache framework that was able to use a special wrapper for weak references to related objects. It can be done--if you really need that level of performance.

 

Paul Baumann

 

 

 

From: GemStone-Smalltalk [[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 08:25
To: [hidden email]
Subject: [GemStone-Smalltalk] Transcient instance variable

 

I'm considering some refactoring that would benefit from having access to transcient instance variables.

Something like SessionTemps, but stored on an instance variable and cleared on each abort or commit.

It would be used to store pending updates. We do that now with a wrapper object, which works fine, 

but the code would be cleaner if the domain object wrapped the pending updates instead.

 

Anything like that available?

 

Thanks for any suggestions,

Bob Nemec

HTS

 


This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.


_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk



This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Transcient instance variable

Gemstone/S mailing list
In reply to this post by Richard Sargent
Richard and Bob,

dbTransient instance variables are only cleared when a persistent object is faulted in from disk/SPC, not at transaction boundaries as Bob appears to need ...

Dale

On Mon, Nov 10, 2014 at 2:56 PM, Richard Sargent via GemStone-Smalltalk <[hidden email]> wrote:
Gemstone/S mailing list wrote
> I'm considering some refactoring that would benefit from having access to
> transcient instance variables.
> Something like SessionTemps, but stored on an instance variable and
> cleared on each abort or commit.
> It would be used to store pending updates. We do that now with a wrapper
> object, which works fine,
> but the code would be cleaner if the domain object wrapped the pending
> updates instead.

Hi Bob,

You could use DbTransient instances in the instance variable. They might do
what you need.
Refer to the Programming Guide. The 3.1 guide has details in section 3.3,
Creating Classes with Special Cases of Persistence.




--
View this message in context: http://forum.world.st/Transcient-instance-variable-tp4789442p4789549.html
Sent from the Gemstone/S mailing list archive at Nabble.com.
_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk


_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Transcient instance variable

Gemstone/S mailing list
In reply to this post by Gemstone/S mailing list
Paul,
Interesting idea: we do need the stored values since we do have domain code that compares updated vs. original.
I do have some concerns. Aside from having to deal with unintended commits (as you mentioned), we would also add some complexity to our development environment. 

Right now we're able use VW with an 'in-memory' domain model. Great for debugging and SUnit tests.  The same domain code runs in the dev VW image as in GS (the deployed web server image has no domain code). If we update the domain instance in VW we'd have to unwind it in a pseudo-abort.

Storing the updates in a data wrapper is working well for us, since the final 'save' is an explicit domain update. We can do a lot of pre-save model validation. Each GS call is a RESTful call that rebuilds the data wrapper model, makes updates, and sends the result. All of which is done with no updates to domain objects until a 'save' is done. Each call is wrapped in a 'current context' that holds all the data wrappers, keyed so that we can find a corresponding wrapper for a domain object.

Most 'read' calls are now under 50ms.  Read + pending updates are 100ms to 200ms, as are the saves. The problem is that some behaviours reference large collections. In the past we wrapped every referenced domain object if there were any pending updates. Now we only wrap those with known pending updates. This introduced a heterogeneous model with wrapped and unwrapped instances. 

Not a problem, but the code would be simpler if there were only domain objects in mix (assuming the impact on performance was minimal).  That's the change I'm looking at.  Right now I'm leaning towards a 'dataWrapper' instVar on the domain root class that we would clear prior to a commit. Having a notNil instVar during a read would be an error condition. 

Bob 


On Monday, November 10, 2014 6:01 PM, Paul Baumann via GemStone-Smalltalk <[hidden email]> wrote:


Hi Bob,
 
It seems like you are currently thinking of using the dataWrapper to note the new change. Consider if it will suit your needs better to use your dataWrapper to note the values before the first inst var change (like with a shallow copy), and allow the change to be made to the inst vars. The shallowCopy can be registered in some manager and the dataWrapper inst var of the copy can refer to the original object.
 
assignDataWrapper
                dataWrapper isNil ifTrue: [
                                dataWrapper := self copy.
                                copy dataWrapper: self.
                                (SessionTemps current at: #ChangeManager ifAbsentPut: [Array new])
                                                add: dataWrapper.
                ].
                ^dataWrapper
 
foo: newValue
                self assignDataWrapper.
                foo := newValue
 
foo
                ^foo
 
This approach performs better because changes to objects are less frequent than access to variables. The cost of making a change is the creation of a copy and adding it to some managed collection. The code needs to make consistent use of the custom setter methods, or else you'd try to hook some generic object dirtying code.
 
The ChangeManager can be defined however you want changes to survive. You can detect changes both at the object level and by the manager. You'd probably either do transaction changes through your ChangeManager or notify the manager afterwards (note that #continueTransaction has a notification bug). If your dataWrapper/copy is a committed object then you'd know that an unmanaged commit happened (the inst vars may now also contain changes from another session). If ChangeManager has copies that differ from what the originals noted for your session then you might have had an unmanaged begin/abort/continue transaction.
 
Regards,
 
Paul Baumann
 
 
From: GemStone-Smalltalk [mailto:[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 16:52
To: [hidden email]
Subject: Re: [GemStone-Smalltalk] Transcient instance variable
 
Paul,
Thanks for the note on 'System rcValueCache'. I can see use using it for other cases.
 
For the 'data wrapper' code change I'm considering performance is key. The check for updated values would be done on each in 'get' method, so anything that slows that down would be noticeable. 
 
I was thinking of code like... (we're still brainstorming this)
 
foo
    ^self getUpdatedValueAt: #foo with: foo
 
getUpdatedValueAt: aKey with: aValue
    self dataWrapper isNil ifTrue: [^aValue].
    ^self dataWrapper getUpdatedValueAt: aKey with: aValue  "where aValue is answered if there are no updates for2 #foo"
 
 
...we may just do this and take responsibility for clearing the data wrapper instVar just prior to a commit.
 
Bob
 
 
 
On Monday, November 10, 2014 12:55 PM, Paul Baumann via GemStone-Smalltalk <[hidden email]> wrote:
 
Re-send to list...
 
From: Paul Baumann
Sent: Monday, November 10, 2014 10:47
...
Subject: RE: [GemStone-Smalltalk] Transcient instance variable
 
Hi Bob,
 
"System rcValueCache" is flushed  with a view/transaction change (commitTransaction, abortTransaction, beginTransaction, continueTransaction). Here is an example that shows view-transient behavior:
 
| anObject get0 get1 |
System beginTransaction.
anObject := Object new.
System rcValueCacheAt: #plbTest put: #remembered for: anObject.
get0 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.
System commitTransaction.
get1 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.
Array with: get0 with: get1
 
anArray( #'remembered', nil)
 
Your domain object can use itself to retrieve by-reference attributes. A view-transient attribute would be like this:
 
viewTransient_plbTest
                ^System rcValueCacheAt: #plbTest for: self otherwise: nil
 
viewTransient_plbTest: anObject
                System rcValueCacheAt: #plbTest put: anObject for: self
 
A session-transient attribute would be like this:
 
sessionTransient_plbTest
                |dict |
                ^(dict := SessionTemps current at: #plbTest otherwise: nil) notNil
                                ifTrue: [dict at: self otherwise: nil].
 
sessionTransient_plbTest: anObject
                (SessionTemps current at: #plbTest ifAbsentPut: [IdentityDictionary new])
                                at: self put: anObject.
 
A wrapper object held in an object attribute can be used for better performance when needed. Your domain object would have an attribute that holds information that can find cached data quicker. If the attribute is nil then the no lookup is necessary (saving a search). If the attribute is not nil then it can be something like a reserved index position within a known location (not strongly referenced) that would contain the data. Years ago I had prototyped a cache framework that was able to use a special wrapper for weak references to related objects. It can be done--if you really need that level of performance.
 
Paul Baumann
 
 
 
From: GemStone-Smalltalk [[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 08:25
To: [hidden email]
Subject: [GemStone-Smalltalk] Transcient instance variable
 
I'm considering some refactoring that would benefit from having access to transcient instance variables.
Something like SessionTemps, but stored on an instance variable and cleared on each abort or commit.
It would be used to store pending updates. We do that now with a wrapper object, which works fine, 
but the code would be cleaner if the domain object wrapped the pending updates instead.
 
Anything like that available?
 
Thanks for any suggestions,
Bob Nemec
HTS
 

This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk



This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk



_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Transcient instance variable

Gemstone/S mailing list

Bob,

 

I'm familiar with that approach. When you have an object-oriented window style where you can open many editors on many objects at once then it is easy to lament the absence of nested transactions. Validation rules tend to be hard part; you want your object model to participate in validation but the window needs to make the changes to the domain for the domain to validate. Sometimes validation needs to happen in both GS and VW. If you have more than simple data structures (like a good object model) then validations are difficult. What you end up doing is creating a framework that simulates nested transactions. A different approach is to use a single managed window (like a notebook) through which all changes are made in a way that fits the traditional GS transaction model.

 

The nested transaction functionality that I'd used (like 10 years ago) would feed changes to the change manager of the active window. The changes are applied to domain objects immediately for their validation. A save of changes can be an abort, replay of changes, and then commit. Each window is aware of its own pending changes and replays changes as necessary for transaction changes. There are probably many important details that I'm forgetting to describe, but this approach was able to work. It sounds like your approach tries let the window see changes on the domain objects that aren't quite there yet. You are creating a generic form of an action model that represents a future change. Like you said, large collections can be a problem because it is the actions made to the collections that you want to play and the generic means of capturing relies on observing differences. I recall collections being a tricky part with the approach that I'd used too. I suspect that collection changes were made with the help of the change manager so that the change manager could know the actions that caused the collection changes.

 

Another way to simulate nested transactions is to use multiple gems. I haven't tried that approach. If you use any (cached) forwarders then GBS would throw errors. There was always too much risk of GBS bugs or cache use restrictions. The well-travelled path is easier to follow; unfortunately that means a window design that never has more than one ongoing set of changes to manage. Consider a window change coordinator that ensures that changes are either saved or aborted before you are able to start making changes in a different window.

 

Regards,

 

Paul Baumann

 

 

From: GemStone-Smalltalk [mailto:[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Tuesday, November 11, 2014 09:29
To: [hidden email]
Subject: Re: [GemStone-Smalltalk] Transcient instance variable

 

Paul,

Interesting idea: we do need the stored values since we do have domain code that compares updated vs. original.

I do have some concerns. Aside from having to deal with unintended commits (as you mentioned), we would also add some complexity to our development environment. 

 

Right now we're able use VW with an 'in-memory' domain model. Great for debugging and SUnit tests.  The same domain code runs in the dev VW image as in GS (the deployed web server image has no domain code). If we update the domain instance in VW we'd have to unwind it in a pseudo-abort.

 

Storing the updates in a data wrapper is working well for us, since the final 'save' is an explicit domain update. We can do a lot of pre-save model validation. Each GS call is a RESTful call that rebuilds the data wrapper model, makes updates, and sends the result. All of which is done with no updates to domain objects until a 'save' is done. Each call is wrapped in a 'current context' that holds all the data wrappers, keyed so that we can find a corresponding wrapper for a domain object.

 

Most 'read' calls are now under 50ms.  Read + pending updates are 100ms to 200ms, as are the saves. The problem is that some behaviours reference large collections. In the past we wrapped every referenced domain object if there were any pending updates. Now we only wrap those with known pending updates. This introduced a heterogeneous model with wrapped and unwrapped instances. 

 

Not a problem, but the code would be simpler if there were only domain objects in mix (assuming the impact on performance was minimal).  That's the change I'm looking at.  Right now I'm leaning towards a 'dataWrapper' instVar on the domain root class that we would clear prior to a commit. Having a notNil instVar during a read would be an error condition. 

 

Bob 

 

On Monday, November 10, 2014 6:01 PM, Paul Baumann via GemStone-Smalltalk <[hidden email]> wrote:

 

Hi Bob,

 

It seems like you are currently thinking of using the dataWrapper to note the new change. Consider if it will suit your needs better to use your dataWrapper to note the values before the first inst var change (like with a shallow copy), and allow the change to be made to the inst vars. The shallowCopy can be registered in some manager and the dataWrapper inst var of the copy can refer to the original object.

 

assignDataWrapper

                dataWrapper isNil ifTrue: [

                                dataWrapper := self copy.

                                copy dataWrapper: self.

                                (SessionTemps current at: #ChangeManager ifAbsentPut: [Array new])

                                                add: dataWrapper.

                ].

                ^dataWrapper

 

foo: newValue

                self assignDataWrapper.

                foo := newValue

 

foo

                ^foo

 

This approach performs better because changes to objects are less frequent than access to variables. The cost of making a change is the creation of a copy and adding it to some managed collection. The code needs to make consistent use of the custom setter methods, or else you'd try to hook some generic object dirtying code.

 

The ChangeManager can be defined however you want changes to survive. You can detect changes both at the object level and by the manager. You'd probably either do transaction changes through your ChangeManager or notify the manager afterwards (note that #continueTransaction has a notification bug). If your dataWrapper/copy is a committed object then you'd know that an unmanaged commit happened (the inst vars may now also contain changes from another session). If ChangeManager has copies that differ from what the originals noted for your session then you might have had an unmanaged begin/abort/continue transaction.

 

Regards,

 

Paul Baumann

 

 

From: GemStone-Smalltalk [[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 16:52
To: [hidden email]
Subject: Re: [GemStone-Smalltalk] Transcient instance variable

 

Paul,

Thanks for the note on 'System rcValueCache'. I can see use using it for other cases.

 

For the 'data wrapper' code change I'm considering performance is key. The check for updated values would be done on each in 'get' method, so anything that slows that down would be noticeable. 

 

I was thinking of code like... (we're still brainstorming this)

 

foo

    ^self getUpdatedValueAt: #foo with: foo

 

getUpdatedValueAt: aKey with: aValue

    self dataWrapper isNil ifTrue: [^aValue].

    ^self dataWrapper getUpdatedValueAt: aKey with: aValue  "where aValue is answered if there are no updates for2 #foo"

 

 

...we may just do this and take responsibility for clearing the data wrapper instVar just prior to a commit.

 

Bob

 

 

 

On Monday, November 10, 2014 12:55 PM, Paul Baumann via GemStone-Smalltalk <[hidden email]> wrote:

 

Re-send to list...

 

From: Paul Baumann
Sent: Monday, November 10, 2014 10:47
...
Subject: RE: [GemStone-Smalltalk] Transcient instance variable

 

Hi Bob,

 

"System rcValueCache" is flushed  with a view/transaction change (commitTransaction, abortTransaction, beginTransaction, continueTransaction). Here is an example that shows view-transient behavior:

 

| anObject get0 get1 |

System beginTransaction.

anObject := Object new.

System rcValueCacheAt: #plbTest put: #remembered for: anObject.

get0 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.

System commitTransaction.

get1 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.

Array with: get0 with: get1

 

anArray( #'remembered', nil)

 

Your domain object can use itself to retrieve by-reference attributes. A view-transient attribute would be like this:

 

viewTransient_plbTest

                ^System rcValueCacheAt: #plbTest for: self otherwise: nil

 

viewTransient_plbTest: anObject

                System rcValueCacheAt: #plbTest put: anObject for: self

 

A session-transient attribute would be like this:

 

sessionTransient_plbTest

                |dict |

                ^(dict := SessionTemps current at: #plbTest otherwise: nil) notNil

                                ifTrue: [dict at: self otherwise: nil].

 

sessionTransient_plbTest: anObject

                (SessionTemps current at: #plbTest ifAbsentPut: [IdentityDictionary new])

                                at: self put: anObject.

 

A wrapper object held in an object attribute can be used for better performance when needed. Your domain object would have an attribute that holds information that can find cached data quicker. If the attribute is nil then the no lookup is necessary (saving a search). If the attribute is not nil then it can be something like a reserved index position within a known location (not strongly referenced) that would contain the data. Years ago I had prototyped a cache framework that was able to use a special wrapper for weak references to related objects. It can be done--if you really need that level of performance.

 

Paul Baumann

 

 

 

From: GemStone-Smalltalk [[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 08:25
To: [hidden email]
Subject: [GemStone-Smalltalk] Transcient instance variable

 

I'm considering some refactoring that would benefit from having access to transcient instance variables.

Something like SessionTemps, but stored on an instance variable and cleared on each abort or commit.

It would be used to store pending updates. We do that now with a wrapper object, which works fine, 

but the code would be cleaner if the domain object wrapped the pending updates instead.

 

Anything like that available?

 

Thanks for any suggestions,

Bob Nemec

HTS

 


This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.


_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk

 


This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.

 

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk

 



This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Transcient instance variable

Gemstone/S mailing list
Just FYI, GemStone/64 v3 does support nested transactions. See methods in class System. For example:

classmethod: System
beginNestedTransaction
"Enter a new nested transaction.
 If session is outside of a transaction, equivalent to beginTransaction.
 Signals a ImproperOperation exception if the begin would exceed16 levels of nested transactions."


Norm



On 11/11/2014 8:16 AM, Paul Baumann via GemStone-Smalltalk wrote:

Bob,

 

I'm familiar with that approach. When you have an object-oriented window style where you can open many editors on many objects at once then it is easy to lament the absence of nested transactions. Validation rules tend to be hard part; you want your object model to participate in validation but the window needs to make the changes to the domain for the domain to validate. Sometimes validation needs to happen in both GS and VW. If you have more than simple data structures (like a good object model) then validations are difficult. What you end up doing is creating a framework that simulates nested transactions. A different approach is to use a single managed window (like a notebook) through which all changes are made in a way that fits the traditional GS transaction model.

 

The nested transaction functionality that I'd used (like 10 years ago) would feed changes to the change manager of the active window. The changes are applied to domain objects immediately for their validation. A save of changes can be an abort, replay of changes, and then commit. Each window is aware of its own pending changes and replays changes as necessary for transaction changes. There are probably many important details that I'm forgetting to describe, but this approach was able to work. It sounds like your approach tries let the window see changes on the domain objects that aren't quite there yet. You are creating a generic form of an action model that represents a future change. Like you said, large collections can be a problem because it is the actions made to the collections that you want to play and the generic means of capturing relies on observing differences. I recall collections being a tricky part with the approach that I'd used too. I suspect that collection changes were made with the help of the change manager so that the change manager could know the actions that caused the collection changes.

 

Another way to simulate nested transactions is to use multiple gems. I haven't tried that approach. If you use any (cached) forwarders then GBS would throw errors. There was always too much risk of GBS bugs or cache use restrictions. The well-travelled path is easier to follow; unfortunately that means a window design that never has more than one ongoing set of changes to manage. Consider a window change coordinator that ensures that changes are either saved or aborted before you are able to start making changes in a different window.

 

Regards,

 

Paul Baumann

 

 

From: GemStone-Smalltalk [[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Tuesday, November 11, 2014 09:29
To: [hidden email]
Subject: Re: [GemStone-Smalltalk] Transcient instance variable

 

Paul,

Interesting idea: we do need the stored values since we do have domain code that compares updated vs. original.

I do have some concerns. Aside from having to deal with unintended commits (as you mentioned), we would also add some complexity to our development environment. 

 

Right now we're able use VW with an 'in-memory' domain model. Great for debugging and SUnit tests.  The same domain code runs in the dev VW image as in GS (the deployed web server image has no domain code). If we update the domain instance in VW we'd have to unwind it in a pseudo-abort.

 

Storing the updates in a data wrapper is working well for us, since the final 'save' is an explicit domain update. We can do a lot of pre-save model validation. Each GS call is a RESTful call that rebuilds the data wrapper model, makes updates, and sends the result. All of which is done with no updates to domain objects until a 'save' is done. Each call is wrapped in a 'current context' that holds all the data wrappers, keyed so that we can find a corresponding wrapper for a domain object.

 

Most 'read' calls are now under 50ms.  Read + pending updates are 100ms to 200ms, as are the saves. The problem is that some behaviours reference large collections. In the past we wrapped every referenced domain object if there were any pending updates. Now we only wrap those with known pending updates. This introduced a heterogeneous model with wrapped and unwrapped instances. 

 

Not a problem, but the code would be simpler if there were only domain objects in mix (assuming the impact on performance was minimal).  That's the change I'm looking at.  Right now I'm leaning towards a 'dataWrapper' instVar on the domain root class that we would clear prior to a commit. Having a notNil instVar during a read would be an error condition. 

 

Bob 

 

On Monday, November 10, 2014 6:01 PM, Paul Baumann via GemStone-Smalltalk <[hidden email]> wrote:

 

Hi Bob,

 

It seems like you are currently thinking of using the dataWrapper to note the new change. Consider if it will suit your needs better to use your dataWrapper to note the values before the first inst var change (like with a shallow copy), and allow the change to be made to the inst vars. The shallowCopy can be registered in some manager and the dataWrapper inst var of the copy can refer to the original object.

 

assignDataWrapper

                dataWrapper isNil ifTrue: [

                                dataWrapper := self copy.

                                copy dataWrapper: self.

                                (SessionTemps current at: #ChangeManager ifAbsentPut: [Array new])

                                                add: dataWrapper.

                ].

                ^dataWrapper

 

foo: newValue

                self assignDataWrapper.

                foo := newValue

 

foo

                ^foo

 

This approach performs better because changes to objects are less frequent than access to variables. The cost of making a change is the creation of a copy and adding it to some managed collection. The code needs to make consistent use of the custom setter methods, or else you'd try to hook some generic object dirtying code.

 

The ChangeManager can be defined however you want changes to survive. You can detect changes both at the object level and by the manager. You'd probably either do transaction changes through your ChangeManager or notify the manager afterwards (note that #continueTransaction has a notification bug). If your dataWrapper/copy is a committed object then you'd know that an unmanaged commit happened (the inst vars may now also contain changes from another session). If ChangeManager has copies that differ from what the originals noted for your session then you might have had an unmanaged begin/abort/continue transaction.

 

Regards,

 

Paul Baumann

 

 

From: GemStone-Smalltalk [[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 16:52
To: [hidden email]
Subject: Re: [GemStone-Smalltalk] Transcient instance variable

 

Paul,

Thanks for the note on 'System rcValueCache'. I can see use using it for other cases.

 

For the 'data wrapper' code change I'm considering performance is key. The check for updated values would be done on each in 'get' method, so anything that slows that down would be noticeable. 

 

I was thinking of code like... (we're still brainstorming this)

 

foo

    ^self getUpdatedValueAt: #foo with: foo

 

getUpdatedValueAt: aKey with: aValue

    self dataWrapper isNil ifTrue: [^aValue].

    ^self dataWrapper getUpdatedValueAt: aKey with: aValue  "where aValue is answered if there are no updates for2 #foo"

 

 

...we may just do this and take responsibility for clearing the data wrapper instVar just prior to a commit.

 

Bob

 

 

 

On Monday, November 10, 2014 12:55 PM, Paul Baumann via GemStone-Smalltalk <[hidden email]> wrote:

 

Re-send to list...

 

From: Paul Baumann
Sent: Monday, November 10, 2014 10:47
...
Subject: RE: [GemStone-Smalltalk] Transcient instance variable

 

Hi Bob,

 

"System rcValueCache" is flushed  with a view/transaction change (commitTransaction, abortTransaction, beginTransaction, continueTransaction). Here is an example that shows view-transient behavior:

 

| anObject get0 get1 |

System beginTransaction.

anObject := Object new.

System rcValueCacheAt: #plbTest put: #remembered for: anObject.

get0 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.

System commitTransaction.

get1 := System rcValueCacheAt: #plbTest for: anObject otherwise: nil.

Array with: get0 with: get1

 

anArray( #'remembered', nil)

 

Your domain object can use itself to retrieve by-reference attributes. A view-transient attribute would be like this:

 

viewTransient_plbTest

                ^System rcValueCacheAt: #plbTest for: self otherwise: nil

 

viewTransient_plbTest: anObject

                System rcValueCacheAt: #plbTest put: anObject for: self

 

A session-transient attribute would be like this:

 

sessionTransient_plbTest

                |dict |

                ^(dict := SessionTemps current at: #plbTest otherwise: nil) notNil

                                ifTrue: [dict at: self otherwise: nil].

 

sessionTransient_plbTest: anObject

                (SessionTemps current at: #plbTest ifAbsentPut: [IdentityDictionary new])

                                at: self put: anObject.

 

A wrapper object held in an object attribute can be used for better performance when needed. Your domain object would have an attribute that holds information that can find cached data quicker. If the attribute is nil then the no lookup is necessary (saving a search). If the attribute is not nil then it can be something like a reserved index position within a known location (not strongly referenced) that would contain the data. Years ago I had prototyped a cache framework that was able to use a special wrapper for weak references to related objects. It can be done--if you really need that level of performance.

 

Paul Baumann

 

 

 

From: GemStone-Smalltalk [[hidden email]] On Behalf Of via GemStone-Smalltalk
Sent: Monday, November 10, 2014 08:25
To: [hidden email]
Subject: [GemStone-Smalltalk] Transcient instance variable

 

I'm considering some refactoring that would benefit from having access to transcient instance variables.

Something like SessionTemps, but stored on an instance variable and cleared on each abort or commit.

It would be used to store pending updates. We do that now with a wrapper object, which works fine, 

but the code would be cleaner if the domain object wrapped the pending updates instead.

 

Anything like that available?

 

Thanks for any suggestions,

Bob Nemec

HTS

 


This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.


_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk

 


This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.

 

_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk

 



This message may contain confidential information and is intended for specific recipients unless explicitly noted otherwise. If you have reason to believe you are not an intended recipient of this message, please delete it and notify the sender. This message may not represent the opinion of Intercontinental Exchange, Inc. (ICE), its subsidiaries or affiliates, and does not constitute a contract or guarantee. Unencrypted electronic mail is not secure and the recipient of this message is expected to provide safeguards from viruses and pursue alternate means of communication where privacy or a binding message is desired.


_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk


_______________________________________________
GemStone-Smalltalk mailing list
[hidden email]
http://lists.gemtalksystems.com/mailman/listinfo/gemstone-smalltalk