Best practices for using GLORP

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

Best practices for using GLORP

Esteban A. Maringolo
So far I like what I found in GLORP. 
Having built a ORM solution in the past (still being used successfully), I still have a few questions about what is the proper way to use GLORP.

1) Should I create a new GlorpSession everytime I want to read/save/update an object?
It is... is a GlorpSession "a window" to the objects persisted in the database. 

2) I know some objects can live outside of a UnitOfWork (UOW), but how do they behave when used inside a UOW.

3) If a UnitOfWork fails, does the session remain usable? or should I throw it away?

I'm going to use GLORP in the context of a Seaside application (where I can have one glorp session per seaside session) and also independently by means of a globally accessible (i.e. singleton) system object. 

4) Can I use the same login object for different database accessors?

5) Can the 31 table name limit be lifted? I know this has to with ORACLE (everybody hates Oracle), but I'm using PostgreSQL, and won't change it for anything else other than something more modern (which is unlikely).

6) Can I map a class name to a column, and an ID to other, and use those two to instantiate an object?
In the ORM I made, you have a special mapping that works together with other kind of mapping, that when combined allow you to have a reference to ANY object.
I.e., you can have N:M relationships stored in a single table.
The tuple would be something like PARENT_CLASS, PARENT_ID, CHILD_CLASS, CHILD_ID.
Of course this can't use the underlying database referential integrity. But after several years using it, it hasn't presented any issues.



Does exist "the good GLORP user guide" somewhere? :-)

So far I like what I see.

Regards,


--
Esteban.













--
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.
Reply | Threaded
Open this post in threaded view
|

Re: Best practices for using GLORP

Tom Robinson
On 9/23/13 5:40 PM, Esteban A. Maringolo wrote:
> So far I like what I found in GLORP.
> Having built a ORM solution in the past (still being used
> successfully), I still have a few questions about what is the proper
> way to use GLORP.
>
> 1) Should I create a new GlorpSession everytime I want to
> read/save/update an object?
> It is... is a GlorpSession "a window" to the objects persisted in the
> database.
Probably not. Creating a new session creates a new descriptor system
instance, (I think) a new DatabaseAccessor, and a new, empty cache. The
biggest factor is the cache, which prevents you from loading duplicate
copies of objects referenced multiple times in an object graph.
Depending on your application, it might make sense to start by using one
session per user transaction, from data retrieval to data update.
>
> 2) I know some objects can live outside of a UnitOfWork (UOW), but how
> do they behave when used inside a UOW.
I don't completely understand what you mean by this. Store (the Cincom
Smalltalk source code management application only creates a unit of work
as a part of writing the new or changed objects to the database).  To
look at Store, you can download the Personal Use version of Cincom
VisualWorks, load the StoreForPostgres parcel and look at the app.
>
> 3) If a UnitOfWork fails, does the session remain usable? or should I
> throw it away?
It should remain usable, but it depends on why the UnitOfWork fails...
>
> I'm going to use GLORP in the context of a Seaside application (where
> I can have one glorp session per seaside session) and also
> independently by means of a globally accessible (i.e. singleton)
> system object.
>
> 4) Can I use the same login object for different database accessors?
Please explain this question further.
>
> 5) Can the 31 table name limit be lifted? I know this has to with
> ORACLE (everybody hates Oracle), but I'm using PostgreSQL, and won't
> change it for anything else other than something more modern (which is
> unlikely).
Where are you seeing this limit?

>
> 6) Can I map a class name to a column, and an ID to other, and use
> those two to instantiate an object?
> In the ORM I made, you have a special mapping that works together with
> other kind of mapping, that when combined allow you to have a
> reference to ANY object.
> I.e., you can have N:M relationships stored in a single table.
> The tuple would be something like PARENT_CLASS, PARENT_ID,
> CHILD_CLASS, CHILD_ID.
> Of course this can't use the underlying database referential
> integrity. But after several years using it, it hasn't presented any
> issues.
You *can* do this. At ESUG 2013, Georg Heeg gave a presentation about
exactly this.  On the other hand, if your application requires much data
at all, this doesn't perform well.
>
>
>
> Does exist "the good GLORP user guide" somewhere? :-)
Cincom ObjectStudio and Cincom VisualWorks both include a pdf Glorp
manual.  There is no Glorp best practices manual at this point.

Hope this helps you a bit....

Regards,

Tom Robinson

--
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.
Reply | Threaded
Open this post in threaded view
|

Re: Best practices for using GLORP

Alan Knight-2
In reply to this post by Esteban A. Maringolo


On 23 September 2013 16:40, Esteban A. Maringolo <[hidden email]> wrote:
So far I like what I found in GLORP. 
Having built a ORM solution in the past (still being used successfully), I still have a few questions about what is the proper way to use GLORP.

1) Should I create a new GlorpSession everytime I want to read/save/update an object?
It is... is a GlorpSession "a window" to the objects persisted in the database. 

Normally you shouldn't. It is a window to the objects, but creating one is relatively expensive. As Tom says, it holds onto a descriptor system and also to a cache. In the model of a client-server application normally you would create a session when the user logs in and discard it when they log out. In a web application you may want to associate one with the user's session. In a really high-volume application where you don't want to keep a per-user cache around you might create and destroy them more frequently. Or more likely, empty the cache and re-use them.
 

2) I know some objects can live outside of a UnitOfWork (UOW), but how do they behave when used inside a UOW.

When you begin a unit of work, any objects which you register have copies made of their state. And any objects you read during the unit of work automatically are registered and have copies made. When you commit the unit of work we compare the state of the objects and write out any differences or any new objects. If the new object isn't reachable from another registered object, then you also need to register it. If you roll back the unit of work, the state of the objects reverts to what it was when the copy was made.


3) If a UnitOfWork fails, does the session remain usable? or should I throw it away?

It's completely usable. However, if the reason it failed was because of an optimistic locking conflict, so the state of the objects in the session is no longer consistent with the database, you may need to refresh the objects. Or, in a web context you could just throw the session away and then re-start, which is roughly the same as throwing away the entire cache and re-reading.  But in general, it's very important to not have references to objects from other sessions. If you do, then you have two objects that Glorp thinks ought to be identical, but they aren't, and much confusion can happen. That's one reason that in a GUI application you rarely discard the session, because you don't want a window open that has a reference to an object that is no longer valid.

 
I'm going to use GLORP in the context of a Seaside application (where I can have one glorp session per seaside session) and also independently by means of a globally accessible (i.e. singleton) system object. 

4) Can I use the same login object for different database accessors?

I think that's fine, but don't have the code in front of me. But it should be pretty clear from the code for Login, which isn't too complicated.
 

5) Can the 31 table name limit be lifted? I know this has to with ORACLE (everybody hates Oracle), but I'm using PostgreSQL, and won't change it for anything else other than something more modern (which is unlikely).

I think that limit only applies when generating table or sequence names. And that limit might be factored out to the platform class anyway. If it isn't, it ought to be.

6) Can I map a class name to a column, and an ID to other, and use those two to instantiate an object?
In the ORM I made, you have a special mapping that works together with other kind of mapping, that when combined allow you to have a reference to ANY object.
I.e., you can have N:M relationships stored in a single table.
The tuple would be something like PARENT_CLASS, PARENT_ID, CHILD_CLASS, CHILD_ID.
Of course this can't use the underlying database referential integrity. But after several years using it, it hasn't presented any issues.

You could probably do this with an AdHocMapping if you wanted it to be utterly arbitrary. But such a mapping will be limited, in that it will be both not very efficient and you couldn't use it across a join. That is, you can write expressions like [:each | each parent something attribute = 3] and have that translated to a join expression. You can't do that if Glorp doesn't know the set of possible values to use in the join. But with an AdHocMapping you ought to be able to do pretty much anything, it's just your responsibility to do the work. A good scary example is StoreDescriptorSystem>>descriptorForStoreBlob:class:. There are also some in GlorpTests, which is a good source of examples.

A more limited form that might work across a join would be a ConditionalMapping, where you enumerate the different possibilities so it knows which tables to join.

Finally an inheritance mapping does a certain amount of this automatically. That is, if you map to a superclass, you have to have a way to identify the subclass rows. So that's more limited, but reasonably well supported
 



Does exist "the good GLORP user guide" somewhere? :-)

There are a few bits of documentation, but they are in various different levels of outdatedness :-(
 

So far I like what I see.

Regards,


--
Esteban.













--
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.

--
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.
Reply | Threaded
Open this post in threaded view
|

Re: Best practices for using GLORP

Esteban A. Maringolo
Hi Alan, Tom,

First of all, thanks for your responses.

I'm developing a system that is not going to be "high demand" but will have around 50 concurrent users. With peaks of REAL concurrence of 20 or so users. Coming through a REST API from a mobile application.

As for the Seaside users, they're going to be a few (around 10) doing backoffice tasks and administration.


I have trouble understanding how the interaction between objects read "outside" of a UOW behave with objects read within a UOW, and if such interaction is recommendable.

Seems to me that a UOW is:
+ a in-memory transaction
+ a database interaction (insert/update/delete) if it finishes ok based on the object "touched" during the UOW

(how is this detected I don't know)


New set of questions:
1) let's suppose I have aCustomer, read in aSession outside of a UOW. So it is "detached" (is this the proper word?) from the UOW.

But the user have some "ToMany" relations, i.e. #orders.
Is it ok to add or remove orders to such customers inside of a a UOW?

E.g.

| customer |
customer := session read: Customer where: [:each | each name = 'Customer 1'].

session inUnitOfWorkDo: [
    | order |
    order := Order forCustomer: customer.
    3 timesRepeat: [order addSampleItem].
    customer addOrder: order.
].

1.a) Is this a good practice?
1.b) Will the order and its items be saved in the database automatically?


2) So, to not be "too detached from reality" (the DB) the session cache should be flushed regularly, otherwise the in-memory cache would diverge from what's in the database and cause conflicts.



3) If I want to be as close as possible to what's in the DB (paying the IO+instantiation penalty) I should work inside a UOW and flush the session cache at the end of each unit of work.

4) Does the framework come with some sort of session/connection pooling?
I'm using the Pharo / OpenDBX version of it.


5) How can I get rid of the 31 characters limit of constraints/table names, etc?
I'm already working with my own subclass of PostgresSQLPlatform


Regards,

Esteban.





















Esteban A. Maringolo


2013/9/25 Alan Knight <[hidden email]>


On 23 September 2013 16:40, Esteban A. Maringolo <[hidden email]> wrote:
So far I like what I found in GLORP. 
Having built a ORM solution in the past (still being used successfully), I still have a few questions about what is the proper way to use GLORP.

1) Should I create a new GlorpSession everytime I want to read/save/update an object?
It is... is a GlorpSession "a window" to the objects persisted in the database. 

Normally you shouldn't. It is a window to the objects, but creating one is relatively expensive. As Tom says, it holds onto a descriptor system and also to a cache. In the model of a client-server application normally you would create a session when the user logs in and discard it when they log out. In a web application you may want to associate one with the user's session. In a really high-volume application where you don't want to keep a per-user cache around you might create and destroy them more frequently. Or more likely, empty the cache and re-use them.
 

2) I know some objects can live outside of a UnitOfWork (UOW), but how do they behave when used inside a UOW.

When you begin a unit of work, any objects which you register have copies made of their state. And any objects you read during the unit of work automatically are registered and have copies made. When you commit the unit of work we compare the state of the objects and write out any differences or any new objects. If the new object isn't reachable from another registered object, then you also need to register it. If you roll back the unit of work, the state of the objects reverts to what it was when the copy was made.


3) If a UnitOfWork fails, does the session remain usable? or should I throw it away?

It's completely usable. However, if the reason it failed was because of an optimistic locking conflict, so the state of the objects in the session is no longer consistent with the database, you may need to refresh the objects. Or, in a web context you could just throw the session away and then re-start, which is roughly the same as throwing away the entire cache and re-reading.  But in general, it's very important to not have references to objects from other sessions. If you do, then you have two objects that Glorp thinks ought to be identical, but they aren't, and much confusion can happen. That's one reason that in a GUI application you rarely discard the session, because you don't want a window open that has a reference to an object that is no longer valid.

 
I'm going to use GLORP in the context of a Seaside application (where I can have one glorp session per seaside session) and also independently by means of a globally accessible (i.e. singleton) system object. 

4) Can I use the same login object for different database accessors?

I think that's fine, but don't have the code in front of me. But it should be pretty clear from the code for Login, which isn't too complicated.
 

5) Can the 31 table name limit be lifted? I know this has to with ORACLE (everybody hates Oracle), but I'm using PostgreSQL, and won't change it for anything else other than something more modern (which is unlikely).

I think that limit only applies when generating table or sequence names. And that limit might be factored out to the platform class anyway. If it isn't, it ought to be.

6) Can I map a class name to a column, and an ID to other, and use those two to instantiate an object?
In the ORM I made, you have a special mapping that works together with other kind of mapping, that when combined allow you to have a reference to ANY object.
I.e., you can have N:M relationships stored in a single table.
The tuple would be something like PARENT_CLASS, PARENT_ID, CHILD_CLASS, CHILD_ID.
Of course this can't use the underlying database referential integrity. But after several years using it, it hasn't presented any issues.

You could probably do this with an AdHocMapping if you wanted it to be utterly arbitrary. But such a mapping will be limited, in that it will be both not very efficient and you couldn't use it across a join. That is, you can write expressions like [:each | each parent something attribute = 3] and have that translated to a join expression. You can't do that if Glorp doesn't know the set of possible values to use in the join. But with an AdHocMapping you ought to be able to do pretty much anything, it's just your responsibility to do the work. A good scary example is StoreDescriptorSystem>>descriptorForStoreBlob:class:. There are also some in GlorpTests, which is a good source of examples.

A more limited form that might work across a join would be a ConditionalMapping, where you enumerate the different possibilities so it knows which tables to join.

Finally an inheritance mapping does a certain amount of this automatically. That is, if you map to a superclass, you have to have a way to identify the subclass rows. So that's more limited, but reasonably well supported
 



Does exist "the good GLORP user guide" somewhere? :-)

There are a few bits of documentation, but they are in various different levels of outdatedness :-(
 

So far I like what I see.

Regards,


--
Esteban.













--
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.

--
You received this message because you are subscribed to a topic in the Google Groups "glorp-group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/glorp-group/pNw3fpPE1wk/unsubscribe.
To unsubscribe from this group and all its topics, 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.

--
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.
Reply | Threaded
Open this post in threaded view
|

Re: Best practices for using GLORP

Alan Knight-2
On 25 September 2013 19:22, Esteban A. Maringolo <[hidden email]> wrote:
Hi Alan, Tom,

First of all, thanks for your responses.

I'm developing a system that is not going to be "high demand" but will have around 50 concurrent users. With peaks of REAL concurrence of 20 or so users. Coming through a REST API from a mobile application.

As for the Seaside users, they're going to be a few (around 10) doing backoffice tasks and administration.


I have trouble understanding how the interaction between objects read "outside" of a UOW behave with objects read within a UOW, and if such interaction is recommendable.

Seems to me that a UOW is:
+ a in-memory transaction
+ a database interaction (insert/update/delete) if it finishes ok based on the object "touched" during the UOW

(how is this detected I don't know)

A UOW is really just the in-memory transaction. However, if you're not already inside a database transaction, then at the end of the UOW it will begin a transaction, write the objects, then commit. If you're already inside a transaction, then it will just write the objets.

It detects if the objects are touched by comparing their current state against the state they had when they were registered with the unit of work. We don't have a portable write barrier across all Smalltalks, so we rely on the user telling us. If you're in VisualWorks there is a write barrier you can use hooked up to Glorp.

So the important interaction is that if an object was read outside of a UOW, then we don't know anything about its state, and the user has to tell us that this object might get modified.



New set of questions:
1) let's suppose I have aCustomer, read in aSession outside of a UOW. So it is "detached" (is this the proper word?) from the UOW.

But the user have some "ToMany" relations, i.e. #orders.
Is it ok to add or remove orders to such customers inside of a a UOW?

E.g.

| customer |
customer := session read: Customer where: [:each | each name = 'Customer 1'].

session inUnitOfWorkDo: [
    | order |
    order := Order forCustomer: customer.
    3 timesRepeat: [order addSampleItem].
    customer addOrder: order.
].

1.a) Is this a good practice?

No. Good practice would be to add the line
   session register: customer.
before doing anything that affects it.
 
1.b) Will the order and its items be saved in the database automatically?

No.
 


2) So, to not be "too detached from reality" (the DB) the session cache should be flushed regularly, otherwise the in-memory cache would diverge from what's in the database and cause conflicts.

That's one strategy. There is an optimistic locking mechanism available to find conflicts, but you still might be presenting the user with stale data. You can also refresh individual objects or groups of objects to ensure they're consistent while still retaining object identity. But for REST users, it might be quite reasonable to just empty the cache between every REST request, or to empty it except for very rarely-changing objects that are always used.


3) If I want to be as close as possible to what's in the DB (paying the IO+instantiation penalty) I should work inside a UOW and flush the session cache at the end of each unit of work.

Yes. Or work inside a transaction.

 

4) Does the framework come with some sort of session/connection pooling?
I'm using the Pharo / OpenDBX version of it.

There is a basic mechanism for connection pooling at the Glorp level in VisualWorks, I don't know if it was ported. It often works better if you can exploit a database-level mechanism for that. But for REST interactions you might reasonably just maintain your own pool of active sessions, which should be very simple.

 
5) How can I get rid of the 31 characters limit of constraints/table names, etc?
I'm already working with my own subclass of PostgresSQLPlatform

Where are you seeing it? I don't know why that should be affecting Postgres. I'd say either try stepping through table creation and see where it's happening, or look for methods with words like maximum and length on their name in the platform hierarchy.

 


Regards,

Esteban.





















Esteban A. Maringolo


2013/9/25 Alan Knight <[hidden email]>


On 23 September 2013 16:40, Esteban A. Maringolo <[hidden email]> wrote:
So far I like what I found in GLORP. 
Having built a ORM solution in the past (still being used successfully), I still have a few questions about what is the proper way to use GLORP.

1) Should I create a new GlorpSession everytime I want to read/save/update an object?
It is... is a GlorpSession "a window" to the objects persisted in the database. 

Normally you shouldn't. It is a window to the objects, but creating one is relatively expensive. As Tom says, it holds onto a descriptor system and also to a cache. In the model of a client-server application normally you would create a session when the user logs in and discard it when they log out. In a web application you may want to associate one with the user's session. In a really high-volume application where you don't want to keep a per-user cache around you might create and destroy them more frequently. Or more likely, empty the cache and re-use them.
 

2) I know some objects can live outside of a UnitOfWork (UOW), but how do they behave when used inside a UOW.

When you begin a unit of work, any objects which you register have copies made of their state. And any objects you read during the unit of work automatically are registered and have copies made. When you commit the unit of work we compare the state of the objects and write out any differences or any new objects. If the new object isn't reachable from another registered object, then you also need to register it. If you roll back the unit of work, the state of the objects reverts to what it was when the copy was made.


3) If a UnitOfWork fails, does the session remain usable? or should I throw it away?

It's completely usable. However, if the reason it failed was because of an optimistic locking conflict, so the state of the objects in the session is no longer consistent with the database, you may need to refresh the objects. Or, in a web context you could just throw the session away and then re-start, which is roughly the same as throwing away the entire cache and re-reading.  But in general, it's very important to not have references to objects from other sessions. If you do, then you have two objects that Glorp thinks ought to be identical, but they aren't, and much confusion can happen. That's one reason that in a GUI application you rarely discard the session, because you don't want a window open that has a reference to an object that is no longer valid.

 
I'm going to use GLORP in the context of a Seaside application (where I can have one glorp session per seaside session) and also independently by means of a globally accessible (i.e. singleton) system object. 

4) Can I use the same login object for different database accessors?

I think that's fine, but don't have the code in front of me. But it should be pretty clear from the code for Login, which isn't too complicated.
 

5) Can the 31 table name limit be lifted? I know this has to with ORACLE (everybody hates Oracle), but I'm using PostgreSQL, and won't change it for anything else other than something more modern (which is unlikely).

I think that limit only applies when generating table or sequence names. And that limit might be factored out to the platform class anyway. If it isn't, it ought to be.

6) Can I map a class name to a column, and an ID to other, and use those two to instantiate an object?
In the ORM I made, you have a special mapping that works together with other kind of mapping, that when combined allow you to have a reference to ANY object.
I.e., you can have N:M relationships stored in a single table.
The tuple would be something like PARENT_CLASS, PARENT_ID, CHILD_CLASS, CHILD_ID.
Of course this can't use the underlying database referential integrity. But after several years using it, it hasn't presented any issues.

You could probably do this with an AdHocMapping if you wanted it to be utterly arbitrary. But such a mapping will be limited, in that it will be both not very efficient and you couldn't use it across a join. That is, you can write expressions like [:each | each parent something attribute = 3] and have that translated to a join expression. You can't do that if Glorp doesn't know the set of possible values to use in the join. But with an AdHocMapping you ought to be able to do pretty much anything, it's just your responsibility to do the work. A good scary example is StoreDescriptorSystem>>descriptorForStoreBlob:class:. There are also some in GlorpTests, which is a good source of examples.

A more limited form that might work across a join would be a ConditionalMapping, where you enumerate the different possibilities so it knows which tables to join.

Finally an inheritance mapping does a certain amount of this automatically. That is, if you map to a superclass, you have to have a way to identify the subclass rows. So that's more limited, but reasonably well supported
 



Does exist "the good GLORP user guide" somewhere? :-)

There are a few bits of documentation, but they are in various different levels of outdatedness :-(
 

So far I like what I see.

Regards,


--
Esteban.













--
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.

--
You received this message because you are subscribed to a topic in the Google Groups "glorp-group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/glorp-group/pNw3fpPE1wk/unsubscribe.
To unsubscribe from this group and all its topics, 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.

--
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.

--
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.
Reply | Threaded
Open this post in threaded view
|

Re: Best practices for using GLORP

Esteban A. Maringolo
Esteban A. Maringolo


2013/9/26 Alan Knight <[hidden email]>

> A UOW is really just the in-memory transaction. However, if you're not already inside a database transaction, then at the end of the UOW it will begin a transaction, write the objects, then commit. If you're already inside a transaction, then it will just write the objets.
>
> It detects if the objects are touched by comparing their current state against the state they had when they were registered with the unit of work. We don't have a portable write barrier across all Smalltalks, so we rely on the user telling us. If you're in VisualWorks there is a write barrier you can use hooked up to Glorp.

Okay, so I should only use UOW if I want to be able to rollback a
change made in memory. But if I already have other method (like
mementos, buffered copies, etc.) to rollback or verify changes before
storing it in the database then I could avoid the use of UOW for
normal things like saving a new customer or adding an order to it (or
simply saving the order).

> So the important interaction is that if an object was read outside of a UOW, then we don't know anything about its state, and the user has to tell us that this object might get modified.

Perfectly clear. Couldn't the write barrier be implemented by means of
immutability? We did that in Dolphin...


>> 1.b) Will the order and its items be saved in the database automatically?
> No.

But if I read the object inside the UOW then it will.

>> 2) So, to not be "too detached from reality" (the DB) the session cache should be flushed regularly, otherwise the in-memory cache would diverge from what's in the database and cause conflicts.

> That's one strategy. There is an optimistic locking mechanism available to find conflicts, but you still might be presenting the user with stale data. You can also refresh individual objects or groups of objects to ensure they're consistent while still retaining object identity. But for REST users, it might be quite reasonable to just empty the cache between every REST request, or to empty it except for very rarely-changing objects that are always used.

So for a REST API, I should implement a pool of sessions (which in
turn could share a pool of connections), and perform a cycle of
aquire, use, flush and release on every HTTP request.

>> 3) If I want to be as close as possible to what's in the DB (paying the IO+instantiation penalty) I should work inside a UOW and flush the session cache at the end of each unit of work.

> Yes. Or work inside a transaction.
Isn't the transaction just a "wrapper" to the underlying DB transaction?

It is... transaction cycle is:
1. Begin DB Transaction
2. Begin UOW
3. Execute transaction block
4. Commit UOW
5. Commit DB Transaction

Is this correct?
Or does the transaction does something related with the session cache?


>> 4) Does the framework come with some sort of session/connection pooling?
>> I'm using the Pharo / OpenDBX version of it.

> There is a basic mechanism for connection pooling at the Glorp level in VisualWorks, I don't know if it was ported. It often works better if you can exploit a database-level mechanism for that. But for REST interactions you might reasonably just maintain your own pool of active sessions, which should be very simple.

The REST sessions are 100% stateless, so a pool like the mentioned
above should do the job.

>> 5) How can I get rid of the 31 characters limit of constraints/table names, etc?
>> I'm already working with my own subclass of PostgresSQLPlatform

> Where are you seeing it? I don't know why that should be affecting Postgres. I'd say either try stepping through table creation and see where it's happening, or look for methods with words like maximum and length on their name in the platform hierarchy.

I'm seeing it ForeignKeyConstraint>>#generateName, it referes to the
Platform #maximumLengthOfTableName, which in PostgreSQLPlatform is set
to 31.

I just tested creating a 60+ FK name and it worked okey in PG 9.3.

As a side not, can I specify a "ON DELETE CASCADE" in the foreign key
definition?


Thanks for your prompt responses.


Regards,

--
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.
Reply | Threaded
Open this post in threaded view
|

Re: Best practices for using GLORP

Alan Knight-2
Sorry, this fell off the radar.

On 26 September 2013 17:25, Esteban A. Maringolo <[hidden email]> wrote:
Esteban A. Maringolo


2013/9/26 Alan Knight <[hidden email]>

> A UOW is really just the in-memory transaction. However, if you're not already inside a database transaction, then at the end of the UOW it will begin a transaction, write the objects, then commit. If you're already inside a transaction, then it will just write the objets.
>
> It detects if the objects are touched by comparing their current state against the state they had when they were registered with the unit of work. We don't have a portable write barrier across all Smalltalks, so we rely on the user telling us. If you're in VisualWorks there is a write barrier you can use hooked up to Glorp.

Okay, so I should only use UOW if I want to be able to rollback a
change made in memory. But if I already have other method (like
mementos, buffered copies, etc.) to rollback or verify changes before
storing it in the database then I could avoid the use of UOW for
normal things like saving a new customer or adding an order to it (or
simply saving the order).

Well, sort of. Except there's no save operation, there's only UOW. That's how Glorp expects to work. And UOW does a bunch of other things as well, like ordering and batching writes, which are problems if you work at the level of saving individual objects. Not to mention dealing with back references.


> So the important interaction is that if an object was read outside of a UOW, then we don't know anything about its state, and the user has to tell us that this object might get modified.

Perfectly clear. Couldn't the write barrier be implemented by means of
immutability? We did that in Dolphin...

Yes, there's a VW package that does precisely that. But it's not portable. So you'd need a version for whichever dialect(s) you're using.  Assuming the immutability has the right semantics.
 
>> 1.b) Will the order and its items be saved in the database automatically?
> No.

But if I read the object inside the UOW then it will.

Yes. Or if you register it before modifying it.
 

>> 2) So, to not be "too detached from reality" (the DB) the session cache should be flushed regularly, otherwise the in-memory cache would diverge from what's in the database and cause conflicts.

> That's one strategy. There is an optimistic locking mechanism available to find conflicts, but you still might be presenting the user with stale data. You can also refresh individual objects or groups of objects to ensure they're consistent while still retaining object identity. But for REST users, it might be quite reasonable to just empty the cache between every REST request, or to empty it except for very rarely-changing objects that are always used.

So for a REST API, I should implement a pool of sessions (which in
turn could share a pool of connections), and perform a cycle of
aquire, use, flush and release on every HTTP request.

Yes.
 

>> 3) If I want to be as close as possible to what's in the DB (paying the IO+instantiation penalty) I should work inside a UOW and flush the session cache at the end of each unit of work.

> Yes. Or work inside a transaction.
Isn't the transaction just a "wrapper" to the underlying DB transaction?

It is... transaction cycle is:
1. Begin DB Transaction
2. Begin UOW
3. Execute transaction block
4. Commit UOW
5. Commit DB Transaction

Is this correct?
Or does the transaction does something related with the session cache?

Yes, the transaction is just a wrapper. The default cycle is.
 1. Begin UOW
 2. Execute transaction block. 
 3. Commit UOW
 4. Start DB transaction.
 5. Write stuff.
 6. Commit DB transaction.

That minimizes long database transactions. But if you manually start a DB transaction first then everything will be done within it, and you'll get a read-consistent view, and commit will fail if there's a conflict. But just reading and then flushing works, though you do have to be aware that you might still have someone else's transaction commit between one of your reads and another if you aren't in a transaction, so you might get an inconsistent view.

Note that the caches are class-specific, so you if you have reference data that very rarely changes and doesn't refer back to transient data you can avoid flushing and re-reading it every time.


>> 4) Does the framework come with some sort of session/connection pooling?
>> I'm using the Pharo / OpenDBX version of it.

> There is a basic mechanism for connection pooling at the Glorp level in VisualWorks, I don't know if it was ported. It often works better if you can exploit a database-level mechanism for that. But for REST interactions you might reasonably just maintain your own pool of active sessions, which should be very simple.

The REST sessions are 100% stateless, so a pool like the mentioned
above should do the job.

>> 5) How can I get rid of the 31 characters limit of constraints/table names, etc?
>> I'm already working with my own subclass of PostgresSQLPlatform

> Where are you seeing it? I don't know why that should be affecting Postgres. I'd say either try stepping through table creation and see where it's happening, or look for methods with words like maximum and length on their name in the platform hierarchy.

I'm seeing it ForeignKeyConstraint>>#generateName, it referes to the
Platform #maximumLengthOfTableName, which in PostgreSQLPlatform is set
to 31.

I just tested creating a 60+ FK name and it worked okey in PG 9.3.

OK, that may have been an old PG limitation, or it may have just inherited that as a default even though it doesn't apply. If it's actually implemented on PostgreSQLPlatform as 31 it seems like that should be updated.

As a side not, can I specify a "ON DELETE CASCADE" in the foreign key
definition?

No. The problem with delete cascade is that it doesn't tell us what it's done, so we have no way to update our information. There's an exclusive attribute you can set on a relationship that says to delete the target when the source is deleted, but it's just doing that the slow way. If you're just throwing away your cache every time you could use cascaded deletes in the database, but there isn't any support for specifying them in the table generation.
 


Thanks for your prompt responses.


Regards,

--
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.

--
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.