Hi again,
Where/how should I create objects that will be available to the entire application or session? The primary example would be a connection pool for a database. Each session needs to have its own connection, which is used throughout the session, and all sessions need to access the application's pool. The tutorials and documentation I've read almost exclusively talk about the leaf objects (components), but don't really discuss how to make changes deeper in the framework. Thanks again, -Rich- _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Rich Warren wrote:
> Hi again, > > Where/how should I create objects that will be available to the entire > application or session? > > The primary example would be a connection pool for a database. Each > session needs to have its own connection, which is used throughout the > session, and all sessions need to access the application's pool. I'm in a similar position to Rich as chance would have it, so I'll tack my query onto his if that's OK. I'm looking for hooks at start/end of request processing to fetch/return db connections to a pool. From googling and rummaging through the source it looks like I should be overriding WARequestHandler>>handleRequest or similar. The mail archives seemed to suggest that if a request didn't complete though I'd need to take further steps to clean up connections. Am I in the right direction here? -- Richard Huxton Archonet Ltd _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Rich Warren
> Where/how should I create objects that will be available to the
> entire application or session? Create a subclass of WASession, you can store everything you need there. In the configuration override the session class of your application to your session class. Philippe _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Philippe Marschall wrote:
> Create a subclass of WASession, you can store everything you need > there. In the configuration override the session class of your > application to your session class. Boy, what timing.. I was working this exact issue last night and couldn't figure out why my session class wasn't doing anything.. I guess I didn't realize that I hadn't really plugged it in, so to speak.. Thanks much for a very timely post! -- rick _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Richard Huxton
Richard Huxton wrote:
> Rich Warren wrote: >> Where/how should I create objects that will be available to the entire >> application or session? > I'm in a similar position to Rich as chance would have it, so I'll tack > my query onto his if that's OK. I'm looking for hooks at start/end of > request processing to fetch/return db connections to a pool. All WAComponent instances (i.e. an instance of your custom subclass) can always find their corresponding session instance via "self session". As Philippe has posted, you can create your own subclass of WASession, to hold your database connection and other objects. I just create a generic session with one instance variable that holds a dictionary. That way I don't have to add/remove new slots, during development. It's somewhat inconvenient to use though (#at: and #at:put: vs. getter/setter). Instead of using the config app., I prefer to code in the #initialize method something like: (self registerAsApplication: 'foobar') preferenceAt: #sessionExpirySeconds put: 8*60*60; preferenceAt: #sessionClass put: PUISession; yourself. To close/release your database connection your WASession subclass should implement an #unregistered method to clean up the connection. #unregistered gets called some time after the session expiry. BTW, Richard, aren't you a regular in postgresql. I had to check what list I was reading. I'm curious what led you to Seaside. Anyhow, welcome aboard. -- Yanni Chiu _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Yanni Chiu wrote:
> All WAComponent instances (i.e. an instance of your custom subclass) > can always find their corresponding session instance via "self session". > As Philippe has posted, you can create your own subclass of WASession, > to hold your database connection and other objects. I just create a > generic session with one instance variable that holds a dictionary. > That way I don't have to add/remove new slots, during development. > It's somewhat inconvenient to use though (#at: and #at:put: vs. > getter/setter). Excellent - that's the sort of thing I was planning to do. Thanks for the pointer. > Instead of using the config app., I prefer to code in the #initialize > method something like: > > (self registerAsApplication: 'foobar') > preferenceAt: #sessionExpirySeconds put: 8*60*60; > preferenceAt: #sessionClass put: PUISession; > yourself. > > To close/release your database connection your WASession subclass > should implement an #unregistered method to clean up the connection. > #unregistered gets called some time after the session expiry. Hmm - that's useful, but what if I want to re-use the connection after the request? That is, I'd like to be able to do: 1. hook a request start, claim a DB connection from the pool 2. do some database queries 3. hook the request end, return connection to the pool Crucially, #3 might restrict that connection for this session only or allow any session to re-use it. I'd hate to have an open transaction floating around for an hour while I wait for the session to expire. > BTW, Richard, aren't you a regular in postgresql. I had to check > what list I was reading. I'm curious what led you to Seaside. > Anyhow, welcome aboard. Yep - that's me. I do a fair bit of web-based application work and I've been looking around for improvements to my way of working. I've looked at Ruby on Rails and Catalyst (Perl) but they mostly seem to handle problems I've already solved in-house. I'm particularly not worried about templating - most of my stuff is code-generated HTML forms, not web-pages as such. Seaside looks very interesting, and I like the interactive debugging, closures continuations etc. Hate the Smalltalk syntax, the way underscore has been stolen and I'm worried about deployment/management and performance issues. It's early days though, so this is still very much first impressions for me. I've got some scripts I'm adapting to generate my Smalltalk model from my DB schema+extras and that to generate forms based on the model via a set of plugin renderers. I've got a different view to most, because I'm viewing my database as the heart of the system, with the application fitting to it rather than the other way around. -- Richard Huxton Archonet Ltd _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Richard Huxton wrote: > Yanni Chiu wrote: > >> All WAComponent instances (i.e. an instance of your custom subclass) >> can always find their corresponding session instance via "self session". >> As Philippe has posted, you can create your own subclass of WASession, >> to hold your database connection and other objects. I just create a >> generic session with one instance variable that holds a dictionary. >> That way I don't have to add/remove new slots, during development. >> It's somewhat inconvenient to use though (#at: and #at:put: vs. >> getter/setter). You still may use getter/setter style calls (with a price of performance) by implementing doesNotUnderstand: aMessage and checking aMessage against keys in your hash. -Dmitry. > > _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Richard Huxton
Richard Huxton wrote:
> Hmm - that's useful, but what if I want to re-use the connection after > the request? I'm not sure I understand you. The connection is still open, and would be reachable via the session. Note that a WASession is longer lived than a single http request/response cycle. > That is, I'd like to be able to do: > 1. hook a request start, claim a DB connection from the pool > 2. do some database queries > 3. hook the request end, return connection to the pool > > Crucially, #3 might restrict that connection for this session only or > allow any session to re-use it. Currently, for postgres, I connect with fixed (by configuration) username/password. Then, on the connection, I send: SET SESSION AUTHORIZATION username But I'm not entirely sure this the "right" thing, since I've not fully tested things with multiple users. > I'd hate to have an open transaction floating around for an hour while I > wait for the session to expire. Why would a transaction be open, unless you designed your application flow in that way. Normally, you'd accumulated user responses in your application model, through multiple interactions with the user. Then when they hit "Save", the transaction would be started, the database changes sent, and the transaction commited. _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Dmitry Dorofeev
Dmitry Dorofeev wrote:
> You still may use getter/setter style calls (with a price of performance) > by implementing doesNotUnderstand: aMessage Yea, but it's not pretty, and IMHO not something to be encouraged. I don't think the performance hit is as big a deal as the confusion it causes when debugging. _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Yanni Chiu
I think Richard's talking about having a pool of connections that are
given out as needed to individual sessions on a per-request basis. Andrew and I did this on one of our projects and wrote the OmniSupport package to help with this kind of thing. Check out http://www.squeaksource.com/OmniSupport.html Unless it's been changed, there should be a subclass of WASession that will give you some idea where you need to plug in. Julian Yanni Chiu wrote: > Richard Huxton wrote: > >> Hmm - that's useful, but what if I want to re-use the connection after >> the request? > > > I'm not sure I understand you. The connection is still open, > and would be reachable via the session. Note that a WASession is > longer lived than a single http request/response cycle. > > > That is, I'd like to be able to do: > >> 1. hook a request start, claim a DB connection from the pool >> 2. do some database queries >> 3. hook the request end, return connection to the pool >> >> Crucially, #3 might restrict that connection for this session only or >> allow any session to re-use it. > > > Currently, for postgres, I connect with fixed (by configuration) > username/password. Then, on the connection, I send: > > SET SESSION AUTHORIZATION username > > But I'm not entirely sure this the "right" thing, since I've > not fully tested things with multiple users. > >> I'd hate to have an open transaction floating around for an hour while >> I wait for the session to expire. > > > Why would a transaction be open, unless you designed your > application flow in that way. Normally, you'd accumulated > user responses in your application model, through multiple > interactions with the user. Then when they hit "Save", the > transaction would be started, the database changes sent, > and the transaction commited. > > _______________________________________________ > Seaside mailing list > [hidden email] > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Yanni Chiu
Yanni Chiu wrote:
> Richard Huxton wrote: >> Hmm - that's useful, but what if I want to re-use the connection after >> the request? > > I'm not sure I understand you. The connection is still open, > and would be reachable via the session. Note that a WASession is > longer lived than a single http request/response cycle. Exactly - my database connection is sat there idle while the user reads the page and decides what to click on. Now sometimes I'm going to want the same connection over multiple requests, but most of the time I don't care. What I don't want to do is reconnect every time, hence the pool. I also don't necessarily want one DB connection for every possibly active session and it doesn't seem worth using pgpool when I've already got persistence. > > That is, I'd like to be able to do: >> 1. hook a request start, claim a DB connection from the pool >> 2. do some database queries >> 3. hook the request end, return connection to the pool >> >> Crucially, #3 might restrict that connection for this session only or >> allow any session to re-use it. > > Currently, for postgres, I connect with fixed (by configuration) > username/password. Then, on the connection, I send: > > SET SESSION AUTHORIZATION username > > But I'm not entirely sure this the "right" thing, since I've > not fully tested things with multiple users. That's what I'm planning, but in 8.1 with "SET ROLE username". With the optional wrinkle perhaps of connecting to the database as a NORMAL or SYSADMIN role at startup - safety in depth. >> I'd hate to have an open transaction floating around for an hour while >> I wait for the session to expire. > > Why would a transaction be open, unless you designed your > application flow in that way. Because mistakes happen, and one of the advantages of having this simple persistence of state is that I can start to span a transaction over multiple request/response if desirable. > Normally, you'd accumulated > user responses in your application model, through multiple > interactions with the user. Then when they hit "Save", the > transaction would be started, the database changes sent, > and the transaction commited. And if I've been clumsy enough to have a stray BEGIN then I can end up blocking other transactions until the session expires. However, if I can attach to the end-of-request-response point in the code then I can issue a ROLLBACK and pretty quickly spot if I've made a mistake. I can also return the connection to the pool if I don't need it and use it for the next request to come in without the overhead of reconnecting/having lots of idle connections. -- Richard Huxton Archonet Ltd _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Julian Fitzell
Julian Fitzell wrote:
> I think Richard's talking about having a pool of connections that are > given out as needed to individual sessions on a per-request basis. That's the sort of thing. A separate connection-pool seems excessive since I have persistence of state available anyway. > Andrew and I did this on one of our projects and wrote the OmniSupport > package to help with this kind of thing. > > Check out http://www.squeaksource.com/OmniSupport.html > > Unless it's been changed, there should be a subclass of WASession that > will give you some idea where you need to plug in. Ta very much - I'll take a look. -- Richard Huxton Archonet Ltd _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Richard Huxton wrote:
> Julian Fitzell wrote: > >> I think Richard's talking about having a pool of connections that are >> given out as needed to individual sessions on a per-request basis. > > That's the sort of thing. A separate connection-pool seems excessive > since I have persistence of state available anyway. If you use the technique in ODBSSession in the code pointed at in Julian's link below, then you have what you need to wrap an http req/resp in a tranaction. But you still need to get a connection from somewhere. The choices seem to be: 1. the session holds it 2. it's gotten from a pool for each transaction 3. a new one is started for each transaction (possibly using pgpool). Is #3 what you mean by "connection pool seems excessive"? >> Andrew and I did this on one of our projects and wrote the OmniSupport >> package to help with this kind of thing. >> >> Check out http://www.squeaksource.com/OmniSupport.html >> >> Unless it's been changed, there should be a subclass of WASession that >> will give you some idea where you need to plug in. I don't wrap my transactions at the http req/resp boundary. I put it at the boundary between the UI and the domain logic. So, in addition to CRUD operations (Create/Read/Update/Delete), I have operations like "parse and load invoice", "create contract from initial invoice", "delete entire contract" and "audit invoice against contract", which are transactional. It seems to me that using the http req/resp as a transaction boundary is an unnecessary and potentially limiting constraint. But as a first cut, just go for it. _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Yanni Chiu wrote:
> Richard Huxton wrote: >> Julian Fitzell wrote: >> >>> I think Richard's talking about having a pool of connections that are >>> given out as needed to individual sessions on a per-request basis. >> >> That's the sort of thing. A separate connection-pool seems excessive >> since I have persistence of state available anyway. > > If you use the technique in ODBSSession in the code pointed at > in Julian's link below, then you have what you need to wrap an > http req/resp in a tranaction. Good - sounds like what I'll need. > But you still need to get a connection from somewhere. > The choices seem to be: > 1. the session holds it Session is the wrong boundary for me (or at least isn't right all of the time). > 2. it's gotten from a pool for each transaction That's what I'm trying to do. > 3. a new one is started for each transaction (possibly > using pgpool). > > Is #3 what you mean by "connection pool seems excessive"? Yep. If I'm running pgpool and memcached anyway then I'll go back to PHP or Perl - I've got plenty of libraries to do form building/validation there. > I don't wrap my transactions at the http req/resp boundary. > I put it at the boundary between the UI and the domain logic. > So, in addition to CRUD operations (Create/Read/Update/Delete), > I have operations like "parse and load invoice", > "create contract from initial invoice", "delete entire contract" > and "audit invoice against contract", which are transactional. > It seems to me that using the http req/resp as a transaction > boundary is an unnecessary and potentially limiting constraint. > But as a first cut, just go for it. I'm not saying the http req/response is the right option for all occasions, but then neither is the session - I'm trying to decouple the two. Hmm - practical example: An update query might fail. I want to do two things: 1. Log the error to the database application_log table. I'll do this as step 1 because I don't know if step 2 will succeed. 2. Recover from/correct the error. At this point I need *two* connections for this one req/response, and connection #2 could span multiple req/response if I needed it to. I've had to do something similar to this in PHP (and work around a bug in the libraries while doing so). Does this make sense, or am I approaching it the wrong way for an object-oriented persistent framework? -- Richard Huxton Archonet Ltd _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Yanni Chiu
Well, it is limiting but keep in mind that the user may have multiple
browser windows open in the same session so if you're going to keep your requests open longer than a request you need some way of making sure you're using the same one (particularly if you are using db transactions). You could probably do it by hooking into something like #isolate: or writing your own similar method so that you'd have a connection within the scope of that call. I think it's implemented as a decoration these days, so the decoration could hold onto the connection. But I'm getting a bit out of my league here in the sense that I haven't acutally tried using seaside with a relational database *and* with db transactions, etc. In our particular case, with the way omnibase works as well, it was easier to have the connections handled automatically for us and provided in the dynamic scope of every request. We actually ended up implementing essentially our own transactional editing mechanism for the model which allowed us to have editing tasks that spanned multiple requests but only wrote to the db in the final request. It all kind of grew into something that worked but I don't think we'd claim it was ideal in hindsight. :) Julian Yanni Chiu wrote: > It seems to me that using the http req/resp as a transaction > boundary is an unnecessary and potentially limiting constraint. > But as a first cut, just go for it. _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Richard Huxton
On 22 févr. 06, at 18:46, Richard Huxton wrote: > Seaside looks very interesting, and I like the interactive > debugging, closures continuations etc. Hate the Smalltalk syntax, I do not want to open a can of worms or a language war but help you. Here are a bunch of tricks I used to explain the syntax Just a trick you put [ ] around expressions when you do not know in advance the number of times it will be executed. (jj isNil) ifTrue: [ ] 4 timesRepeat: [ ] #(1 2 3) do: [:each | ...] separatedBy: [ ] Then you only put parenthese to distinguish between messages having the same priority: kkk isNil ifTrue: [] (kkk includes: #a) ifTrue: [] In fact I kind of really like the syntax. aDict at: #stef put: #fun > the way underscore has been stolen and I'm worried about deployment/ > management and performance issues. It's early days though, so this > is still very much first impressions for me. In Squeak 3.9 the _ is given back to you :). For deployment and management, the gurus around me (lukas and adrian) are really enjoying the online, on the fly update of code while the system is running.... Now do not hesitate to give feedback and code to the community, we will pay attention that it is not lost Stef _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Yanni Chiu
Yanni,
I've been looking at my production code, based on (I think) version 7.x of the PostgreSQL client. Long ago, I was forced to wrap the PGConnection>>execute: method in a semaphore. Otherwise, concurrent accesses (like commonly happens in a web server) would confuse the driver. Does this ring a bell to you? Do your later versions correct the concurrency issues I had encountered? Nevin _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Richard Huxton
Richard Huxton wrote:
>> 2. it's gotten from a pool for each transaction > > That's what I'm trying to do. That same code that Julian pointed at has a pool implementation you could probably adapt. I didn't look at it very closely. > Yep. If I'm running pgpool and memcached anyway then I'll go back to PHP > or Perl - I've got plenty of libraries to do form building/validation > there. Not sure I get the point here. How are connection pooling and object/row caching inter-related? Are you talking about object caching (i.e. as in a typical Object-Relational mapping framework)? > Hmm - practical example: An update query might fail. I want to do two > things: > 1. Log the error to the database application_log table. I'll do this as > step 1 because I don't know if step 2 will succeed. > 2. Recover from/correct the error. > > At this point I need *two* connections for this one req/response, and > connection #2 could span multiple req/response if I needed it to. I've > had to do something similar to this in PHP (and work around a bug in the > libraries while doing so). > > Does this make sense, or am I approaching it the wrong way for an > object-oriented persistent framework? Makes a lot of sense to me. That's the typical application logic to be implemented, once you get beyond row updates. I'm somewhat hesitant to suggest an Object-Relational mapping framework (e.g. Glorp), because IMHO an O-R framework's complexity and constraints can become a bigger problem than the original application problem you were trying to solve. For example, you usage of two connections might require some shoehorning. _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Nevin Pratt
Nevin Pratt wrote:
> I've been looking at my production code, based on (I think) version 7.x > of the PostgreSQL client. Long ago, I was forced to wrap the > PGConnection>>execute: method in a semaphore. Otherwise, concurrent > accesses (like commonly happens in a web server) would confuse the driver. > > Does this ring a bell to you? Do your later versions correct the > concurrency issues I had encountered? The only bell it rings is that the Swiss guys had a concurrency issue (~2 years ago), which they eventually tracked down to a concurrency issue in their code. IIRC, the failure scenario was that the postmaster just terminated the connection because it got interleaved bytes coming down the socket. Otherwise, the code and most especially the underlying state machine is mostly unchanged. You can only call execute: in one thread, an await its completion, before issuing another call. _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Rich Warren
| It seems to me that using the http req/resp as a transaction
| boundary is an unnecessary and potentially limiting constraint. The problem is: even though you don't see the http req/resp mechanism from your Seaside app, it still is there. And the user may decide at any point to do something unexpected, like hitting the back button, abandoning a session. So when your database transaction spans multiple web pages and the user breaks out of your planned path... *boom* you end up with left open transactions, transaction starts in already open transactions and whatnot... I asked something similar some time ago, and the general consensus was to do all database accesses in a single open transaction, do stuff, close transaction manner. Regards, mjl _______________________________________________ Seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Free forum by Nabble | Edit this page |