[Seaside] Mongo/Voyage singleton vs instance mode and seaside Request filters

Sabine Manaa manaa.sabine at gmail.com
Thu Feb 16 13:03:17 UTC 2017


This is the slack discussion.
Please answer to the first mail and not to this one. I hope this is ok, i
put it here for that it is not lost after 10.000 messages...

Sabine Manaa [10:34 AM]
I want to run mongo voyage in *instance* mode (each of my customers will
have its own repository). How can I tell voyage to save my object in a
certain repository (= mongo database)?
In the enterprise pharo book, it is written that "save" writes in the
singleton mode directory (this works fine, e.g. `RKAPerson new save`).
But for using a certain repository it should be `RKAPerson new save:`
acccording to the book.
I dont get it how to do this, to me it seems that save: implementation in
Object is missing. @udos  or @norbert.hartl  or @estebanlm  can you tell me
how to do this?

Esteban Lorenzano [10:35 AM]
you cannot use it as is

[10:35]
you will need to create something particular

[10:35]
I implemented something like that some time ago

[10:35]
idea was to write a seaside filter

[10:35]
then in the filter simething like:

[10:36]
 ```self
    useRepository: self repositoryForClient
    during: [ self handleNext: aRequest ]```

[10:37]
and you can implement:

[10:38]
 ```repositoryForClient
    ^ self session propertyAt: #repository ifAbsentPut: [ VOMongoRepository
database: 'client-database' ]```
(edited)

[10:38]
then

[10:40]
 ```useRepository: aRepository during: aBlock
    | oldRepository |
   oldRepository := VORepository current.
   VORepository setRepository: aRepository.
   aBlock ensure: [ VORepository setRepository: oldRepository ]```

[10:40]
voilà, that will work

[10:41]
(of course, is more or less like that, not exactly… but you get the idea)

Sabine Manaa [10:42 AM]
Hi Esteban, thanks for answering so fast, it is very good for me so I can
proceed with my work. I understand that I would keep the repository of the
user in the session and each time i do a database operation, the repository
will be changed.

I ask myself if this will lead to problems/if this will work if there are
many users... I can not imagine that for each operation switching the
repository is the right way?

Esteban Lorenzano [10:43 AM]
it will work because each request is handled separately

Sabine Manaa [10:43 AM]
So would you recommend, *not* to create a repository for each of my users?
(One user is a *company* with N persons)

Esteban Lorenzano [10:43 AM]
no, is ok

[10:43]
is a valid approach

[10:43]
now

Sabine Manaa [10:43 AM]
ok. I will try it! Thanks a lot! btw: the book is wrong at this place.

Esteban Lorenzano [10:44 AM]
if you have a company with +1 users

[10:44]
and you want a database for each company

Sabine Manaa [10:44 AM]
yes, i think there are a lot of advantages in the future when I do it like
this

Esteban Lorenzano [10:44 AM]
then you could want to keep your repositories other place than session

[10:44]
but that’s another story

[10:45]
for start, that's ok :slightly_smiling_face:

[10:45]
and the problem with this

Sabine Manaa [10:45 AM]
i dont understand „keep your repositories other place than session“ ->
repository is in database, session in my image

Esteban Lorenzano [10:45 AM]
is you need the relation user-database somewhere

Sabine Manaa [10:46 AM]
i keep it in a „super-database“ for lookup

Esteban Lorenzano [10:46 AM]
ok :slightly_smiling_face:

Sabine Manaa [10:46 AM]
ok, I will try it and come back if it does not work, thanks again!

Esteban Lorenzano [10:47 AM]
welcome

Sabine Manaa [10:47 AM]
do you want me to put the book.is-wrong- information somewhere? for that it
will be changed?

Esteban Lorenzano [11:04 AM]
oops

[11:04]
I just remember that you need to do *another step* to make that work
:stuck_out_tongue:

[11:05]
and what part of the book does not work?

Sabine Manaa [11:05 AM]
page 156: „Second is Instance mode. In Instance mode, the first argument is
always the repository on which to perform the operation.
Persisting Objects with Voyage
save:
remove:
removeAll:
selectAll:
selectOne:where:
selectMany:where:
stores an object into repository (insert or update) removes an object from
repository
removes all objects of class from repository retrieves all objects of some
kind
retrieves first object that matches the where clause retrieves all objects
that matches the where clause"

Esteban Lorenzano [11:06 AM]
what’s bad with that?

Sabine Manaa [11:07 AM]
there is no save: in Object.

[11:07]
this methods are missing.

Esteban Lorenzano [11:07 AM]
it is correct

Sabine Manaa [11:07 AM]
that was my question

Esteban Lorenzano [11:07 AM]
nope

[11:07]
they are in repository

[11:07]
“instance mode"

[11:07]
means you keep an instance of repository

[11:07]
and instead using

[11:07]
 ```myObject save.```

[11:07]
you use

[11:08]
 ```repository save: myObject```

Sabine Manaa [11:08 AM]
aha!

Esteban Lorenzano [11:08 AM]
but well… still on the missing part

Sabine Manaa [11:08 AM]
so, I dont need  VORepository using: (VOMongoRepository database: 'xxx')
do: [ self save ].

Esteban Lorenzano [11:09 AM]
you will need a “thread local” strategy

Sabine Manaa [11:09 AM]
(using:do: is the existing implementation of useRepository: aRepository
during: aBlock )

Esteban Lorenzano [11:10 AM]
ah yes

Sabine Manaa [11:10 AM]
ok

[11:10]
“thread local” ?

Esteban Lorenzano [11:10 AM]
nevertheless I’m not sure it will work

Sabine Manaa [11:11 AM]
ok, i am listening

Esteban Lorenzano [11:11 AM]
I implemented that

[11:11]
it should be there

[11:11]
I will recover and commit

[11:11]
thing is

[11:11]
since you can have a thread switch

[11:11]
when multiple requests

[11:12]
if you just change the singleton, it will not work

[11:12]
for that is used a "thread local” (storing the var on the current thread)
(edited)

[11:13]
but the easiest way is to use the stack

[11:13]
which is store the value in a dynamic variable

[11:14]
and access it when required :stuck_out_tongue:

[11:15]
I will do it, don’t worry

Sabine Manaa [11:15 AM]
:smiley:

[11:16]
so, for now, can I just use `repository save: myObject` (we are not yet
online, so no problem with data...)

[11:16]
?

[11:17]
Did I understand right, that I dont need `VORepository using:
(VOMongoRepository database: 'xxx') do: [ self save ].` as discussed first?

[11:17]
and I would NOT turn on singleton mode (edited)

Esteban Lorenzano [11:17 AM]
yes

[11:17]
you can keep the repository on your user’s session

[11:17]
and take it each time

[11:18]
and not use singleton mode

[11:18]
but using a filter and keeping singleton mode is a lot more confortable

[11:18]
I wonder if you know the notion of seaside filters?

Sabine Manaa [11:18 AM]
using a filter? no i dont!

Esteban Lorenzano [11:19 AM]
that’s why you didn't got the original idea :slightly_smiling_face:

[11:19]
so you can create seaside filters

[11:19]
any kind

[11:19]
and declare your app with your filters

[11:20]
(that’s how seaside works, by the way: it stacks filters and “rendering” is
just one kind of a filter)

[11:20]
the idea is that you add your “repository filter”

[11:21]
and you use your app is if it would be just a singleton

Sabine Manaa [11:21 AM]
and because it knows the session it automatically knows the repository when
saving?

Esteban Lorenzano [11:21 AM]
the app will behave as is

[11:21]
if you do it as I explained

[11:21]
during the execution of your request

[11:21]
the “singleton” will be what you want

Sabine Manaa [11:23 AM]
for my understanding: eg curently user clicks on „save“, then i have an
ajax request. in this ajax request, i currently do `aPersonInstance save`

[11:23]
where is the point where the seaside filter will grab this?

Esteban Lorenzano [11:26 AM]
a lot before

[11:26]
filter is before

Udo Schneider [11:27 AM]
You can also extend `WASession` to store the repo on a per session base.
Then this would become `self session repository save: aPersonInstance` in a
`WAComponent`. Outside of it you might have to resort to
`WACurrentRequestContext value repository save: aPersonInstance`. This of
course implies that your `WASession` subclass implements `#repository`.

Esteban Lorenzano [11:28 AM]
is the same

[11:28]
you will always need to store repository in your session

Sabine Manaa [11:28 AM]
the same as estebans 2nd idea without the filter thing

[11:29]
yes this is clear to me and no problem. But the thing with the filter is
better?

[11:30]
store the repo on a per session base was my initial plan.

Esteban Lorenzano [11:30 AM]
the filter will allow you to use “singleton mode” in a non singletonnish
context

[11:30]
personally I like

[11:31]
to do

[11:31]
 ```Person selectOne: [ … ]```

[11:31]
etc.

[11:31]
instead

[11:31]
 ```repository selectOne: Person where: [ … ]```
(edited)

[11:31]
but is a matter of taste :wink:

Sabine Manaa [11:33 AM]
can you give me an entry point how to define a filter (where to call the
above `using:do:`)

[11:33]
how to initiate the filter

[11:33]
and can it be used within an ajax request?

Udo Schneider [11:35 AM]
Another idea is to define your own `DynamicVariable` subclass - e.g.
`CurrentVoyageRepository` which `#default`s to: `[ WACurrentRequestContext
value session repository ] on: WARequestContextNotFound do: [ :ex | nil ]`.
This will also allow you to use a "current" Repository outside of Seaside.
E.g. for background jobs: `CurrentVoyageRepository value: backgroundRepo
during: [self doSomething: WACurrentVoyageRepository  value]`.

Esteban Lorenzano [11:36 AM]
that’s what I was explaining, @udos  :slightly_smiling_face:


Udo Schneider [11:36 AM]
sorry - too many messages. Information overflow :slightly_smiling_face:

[11:38]
`DynamicVariable` is one of those things which "smells global". But it
really solves some otherwise ugly designs ...

Sabine Manaa [11:38 AM]
At the moment it seems to me that in my case it is sufficient to use the
session-solution. I always have the person, trip etc in my session and if I
also have the repository (which is always the same for one person/company),
I can say `aRepository save: aPerson` . But I can not definifely say it
because I did not yet use filters.

Esteban Lorenzano [11:39 AM]
So you do a

[11:39]
child of

[11:39]
WARequestFilter

[11:39]
and redefine

[11:39]
 ```handleFiltered: aRequestContext
    VOCurrentRepository
        value: self obtainSessionRepository
        during: [  self next handleFiltered:  aRequestContext]

obtainSessionRepository
    ^ self session
    propertyAt: #repository
    ifAbsentPut: [ self newRepositoryForUser ]

newRepositoryForUser
   ^  VOMongoRepository database: 'client-database'
```
(edited)

Sabine Manaa [11:41 AM]
and in the ajax request, is this context also given?

Esteban Lorenzano [11:42 AM]
now, for that to work, you will need something I need to commit
:slightly_smiling_face:

[11:42]
give me half an hour

Sabine Manaa [11:42 AM]
:grinning:

Esteban Lorenzano [11:43 AM]
it was already implemented, just lost in the limbo

Sabine Manaa [11:45 AM]
indeed - I have an idea now and then I would not need to change my code (my
senders of save eg). this is great and very comfortable.
:slightly_smiling_face: (edited)

[11:47]
could you make a version on github?

Esteban Lorenzano [12:33 PM]
so well

[12:33]
I committed to voyage

[12:34]
check VOCurrentRepository

[12:34]
and VODynamicContainer

[12:34]
(I just do versions on github now :P)

Sabine Manaa [12:35 PM]
great, i will wait for the versions on github. I the meantime I started to
implement the other stuff.

[12:38]
one question: Am I right that i will switch to instance mode with the
filter solution?

Esteban Lorenzano [12:52 PM]
it is committed on master

Sabine Manaa [12:53 PM]
are you planning to make a version, too?

Sabine Manaa [1:11 PM]
Esteban, thank you very much for your work. I  have to finish for today. I
have all my test data creation prepared now in different repositories with
authentication and a WARequestFilter subclass: #RKARequestFilter added to
the application. Tomorrow I will try what it does with your todays changes
in the ajax requests :slightly_smiling_face:

Pierce Ng [1:26 PM]
if #obtainSessionRepository is doing self session propertyAt:ifAbsentPut:
then this is similar to subclassing WASession?

Sabine Manaa [1:29 PM]
>>obtainSessionRepository is a method in my WARequestFilter subclass:
#RKARequestFilter

[1:30]
and I assigned the request filter to my application in sth like:

[1:30]
`    theApplication := WAAdmin register: RKALayoutView asApplicationAt:
theApplicationName.
    ...
    theApplication
        addLibrary: RKAConfiguration library;
        preferenceAt: #sessionClass put: RKASession;
        preferenceAt: #rootClass put: RKATask;
        *addFilter: RKARequestFilter new.*` (edited)

[1:31]
I also have a subclass of WASession but this is another topic imho

Pierce Ng [1:31 PM]
so your RKASession can also figure out by looking at your super database
which user should use which repository, yes?

Sabine Manaa [1:32 PM]
yes

Esteban Lorenzano [1:33 PM]
Pierce Ng
if #obtainSessionRepository is doing self session propertyAt:ifAbsentPut:
then this is similar to subclassing WASession?
Posted in #databasesYesterday at 1:26 PM

[1:33]
yes

[1:33]
just you don’t need to subclass it :slightly_smiling_face:

Pierce Ng [1:35 PM]
i have been doing WASession subclasses and  was just thinking i don't want
to deal with one subclass per application. will try this.
:slightly_smiling_face: thanks.

Torsten Bergmann [2:57 PM]
I agree with Esteban:

Person selectOne: [ … ]

is easier to write and understand than:

repository selectOne: Person where: [ … ]

On the other side I'm not sure if the second expression style by sending
messages to the repo is better especially in debugging situations
(evaluating the code on a specific production or "fake" repository to check
a production situation easier)

Udo Schneider [3:02 PM]
I used both approaches - and yes the singleton-mode style is simpler.
However simplifying code from instance-mode to singleton-mode is easy (once
you know singleton mode will be sufficient). Going the other way is PITA -
been there, done that. Everything I do nowadays uses the instance mode
initially. But with Estebabans addition it seems you can eat the cake and
keep it ... so using multiple repos while maintaining the simpler API of
singleton-mode.

Torsten Bergmann [3:11 PM]
I like that (if one starts with singleton mode) one could easily add
multiple repos with data separation later (to separate customers/user
groups of the application).

Sabine Manaa [3:13 PM]
@astares: me too and this is my case. Waiting for tomorrow morning working
on this again....


----- Today February 16th, 2017 -----
Sabine Manaa [9:03 AM]
Hi @estebanlm
`handleFiltered:` of my WARequestFilter subclass is done *before*
registering of the new session in
`WAApplication handleDefault:
self handle: aRequestContext registering: self newSession`.
So in `obtainSesionRepository`, the session is not yet initialized.  Did I
miss something? (edited)

Esteban Lorenzano [9:12 AM]
mmm

[9:12]
that’s not good

[9:14]
I would send a mail to seaside list, I’m afraid I do not remember exactly
how to solve that

[9:19]
maybe there is a way to declare the filter *after* the session has been
pushed

Sabine Manaa [9:32 AM]
ok, I will have a look first and if I dont find it, I will ask in the
mailinglist!

Sabine Manaa [12:47 PM]
question: If I follow the idea from @udos and simply overwrite the save
method (all my objects to be saved have one `RKAObject` superclass) with

[12:47]
RKAObject>>save
    | theRepository |
    theRepository := [ WACurrentRequestContext value session
customerRepository ]
        on: WARequestContextNotFound
        do: [ :ex | nil ].
    theRepository save: self. (edited)

[12:48]
and set the repository of the session when login

[12:49]
I have the same result - each object is saved in its belonging
customerRepository

[12:50]
Is there a disadvantage against the solution with the Filter subclass?

Stephan Eggermont [1:28 PM]
@sabine: it would be useful to have a summarizing post to the mailing list.
Would you be able to find the time for that?

Sabine Manaa [1:29 PM]
@stephan do you mean that the discussion would be useful for others? If
yes, I would do it.

[1:29]
tomorrow

[1:30]
also because of the 10.000 messages limit

[1:30]
of slack

Stephan Eggermont [1:41 PM]
Yes, it looks useful

Sabine Manaa [1:41 PM]
ok, i just started and will post it

Stephan Eggermont [1:59 PM]
Great

2017-02-16 14:00 GMT+01:00 Sabine Manaa [via Smalltalk] <
ml-node+s1294792n4934578h13 at n4.nabble.com>:

> Hi,
>
> the last days we had a an interesting discussion on slack.
> Stephan Eggermont asked me to post a summary here for discussion,
> Information and because of the 10.000 messages limitation of slack:
>
> Situation:
> Til now I used Mongo/Voyage in singleton mode [1]. That means in short
> that I had one single database/repository in mongo. I then decided that I
> want to have one database/repository per customer (one customer has n
> persons) and one "super-database/repository" for data like currencies valid
> for all customers and for lookup of the right database for each user/email
> address. This has a lot of advantages. Mongo allows to have n
> databases/repositories within one installation.
>
> First, I thought, that I have to change my code from
>
> "aPersonInstance save"
> (this is the singleton mode way, always same repository)
>
> to "aCustomerRepository save: aPersonInstance"
> (instance mode - so that mongo knows which repository to use)
>
> Then Esteban proposed to use a SeasideFilter. This would be a lot better.
> He told me to create a subclass of WARequest Filter and implement:
>
> "handleFiltered: aRequestContext
>     VOCurrentRepository
>         value: self obtainSessionRepository
>         during: [  self next handleFiltered:  aRequestContext]
>
> obtainSessionRepository
>     ^ self session
>     propertyAt: #repository
>     ifAbsentPut: [ self newRepositoryForUser ]
>
> newRepositoryForUser
>    ^  VOMongoRepository database: 'client-database'"
>
> He also implemented and released some more code for this (e.g.
> VOCurrentRepository) [2].
> The idea/concept is, that seaside uses the right repository within the
> current request context  and one does not have to deal with the
>  repositories.
>
> Udo Schneider suggested the DynamicVariable subclass and get it within the
> WACurrentRequestContext
>
> I did not know/use about Seaside Filters and not about DynamicVariables,
> so it was very interesting.
>
> Currently I think that perhaps for me overwriting the save method (I have
> a superclass of all my voyage root objects) would be sufficient for me. My
> customers repository is set at login and kept in the session.
>
> RKAObject>>save
>     | theRepository |
>     theRepository := [ WACurrentRequestContext value session
> customerRepository ]
>         on: WARequestContextNotFound
>         do: [ :ex | nil ].
>     theRepository save: self.
>
> But I am not finished with this and opinions are welcome and perhaps this
> is useful for others.
>
> There was a lot more discussion. I will put the whole discussion from
> slack in an answer of this post. So it is kept but not attached in answers
> to THIS post.
>
> Sabine
>
>
>  [1] http://files.pharo.org/books-pdfs/entreprise-pharo/2016-10-
> 06-EnterprisePharo.pdf chapter on voyage
> [2]https://github.com/pharo-nosql/voyage/commit/
> d1fa97e41470671452d3501421a7bd0ac60456e9
>
>
> ------------------------------
> If you reply to this email, your message will be added to the discussion
> below:
> http://forum.world.st/Mongo-Voyage-singleton-vs-instance-
> mode-and-seaside-Request-filters-tp4934578.html
> To start a new topic under Seaside General, email
> ml-node+s1294792n86180h75 at n4.nabble.com
> To unsubscribe from Seaside, click here
> <http://forum.world.st/template/NamlServlet.jtp?macro=unsubscribe_by_code&node=1310907&code=bWFuYWEuc2FiaW5lQGdtYWlsLmNvbXwxMzEwOTA3fC0xOTE3OTcxOTg5>
> .
> NAML
> <http://forum.world.st/template/NamlServlet.jtp?macro=macro_viewer&id=instant_html%21nabble%3Aemail.naml&base=nabble.naml.namespaces.BasicNamespace-nabble.view.web.template.NabbleNamespace-nabble.view.web.template.NodeNamespace&breadcrumbs=notify_subscribers%21nabble%3Aemail.naml-instant_emails%21nabble%3Aemail.naml-send_instant_email%21nabble%3Aemail.naml>
>




--
View this message in context: http://forum.world.st/Mongo-Voyage-singleton-vs-instance-mode-and-seaside-Request-filters-tp4934578p4934580.html
Sent from the Seaside General mailing list archive at Nabble.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/seaside/attachments/20170216/e36cfd2a/attachment-0001.html>


More information about the seaside mailing list