-
-
Save doctrinebot/33cf72b426f1a3d70421 to your computer and use it in GitHub Desktop.
Attachments to Doctrine Jira Issue DDC-720 - https://github.com/doctrine/doctrine2/issues/5234
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
amirabiri: Is there a way to ask Doctrine (2.0) to flush(), but only for a given entity and any entities that can be reached from it? | |
amirabiri: rather than flush() any modified entity that's currently in memory? | |
amirabiri: guilhermeblanco: ? | |
guilhermeblanco: no... you need to use 2 EM to achieve that | |
amirabiri: guilhermeblanco: then let me ask you this: what's the best way to avoid a mid-change flush() ? in other words I have a service object A which starts manipulating an entity, but before it calls flush(), it calls a method on object B, which for some reason calls another method on object A again, which starts another change entity -> flush() sequence | |
amirabiri: the result would be that the first entity would get half-flushed | |
amirabiri: What is the correct way to avoid that? | |
amirabiri: guilhermeblanco: ? | |
guilhermeblanco: amirabiri: I didn't get the issue | |
guilhermeblanco: are you double flushing in a recursive way? | |
amirabiri: In my architecture there are "service" objects | |
guilhermeblanco: this should never happen | |
guilhermeblanco: amirabiri: the service layer should do atomic things... | |
guilhermeblanco: and not multiple flushings | |
guilhermeblanco: in a 4-layer architecture, it deals with atomic items... like include a new user (also includes addresses, IMs, etc) | |
amirabiri: guilhermeblanco: Exactly, so I'm trying to understand how to achieve this atomicness | |
guilhermeblanco: amirabiri: the basic idea is: | |
guilhermeblanco: doWhatYourServiceIsSupposedToDo($em); $em->flush(); | |
guilhermeblanco: so... you should never call flush() during service execution | |
amirabiri: yes, but there are two theorethical problems with this: | |
amirabiri: 1) when WhatYourServiceIsSupposedToDo() is called, the EM might be dirty | |
guilhermeblanco: just think over your architecture... remember it is recommended to have a single call to flush() in your entire script execution | |
guilhermeblanco: amirabiri: exactly... | |
guilhermeblanco: and it should be like that | |
amirabiri: 2) WhatYourServiceIsSupposedToDo might need to call another service to perform its job, which leads to a whole execution path which might lead back to another call to the service object, say WhatYourServiceIsSupposedToDo2(), which might want to call flush() as well | |
amirabiri: guilhermeblanco: so in your mind who should be calling $em->flush()? the controller? | |
guilhermeblanco: the controller should call the em->flush | |
amirabiri: Well that creates 2 problems: | |
amirabiri: 1) the controller should be a simple being that is not aware of the existence of a persistence layer but only talk to the service layer | |
amirabiri: 2) The controller has no way to know what the correct way to breakdown the request to atomic parts should be - the service objects are the only ones that should know that | |
guilhermeblanco: amirabiri: ya, you're right... | |
guilhermeblanco: I tend to think too much over APIs... | |
guilhermeblanco: when I imagine an app... I usually imagine the REST server as a consumer of service layer | |
guilhermeblanco: so... sometimes I made mistakes | |
guilhermeblanco: but if you consider it like that... | |
guilhermeblanco: each service layer has its own EM | |
guilhermeblanco: so each access creates its own EM | |
guilhermeblanco: and consumes its own | |
guilhermeblanco: so.. it never messes with other EMs | |
amirabiri: We've reached that conclusion ourselves so far | |
amirabiri: but services still need to talk to each other | |
amirabiri: which leaves us with problem #2: | |
amirabiri: amirabiri: 2) WhatYourServiceIsSupposedToDo might need to call another service to perform its job, which leads to a whole execution path which might lead back to another call to the service object, say WhatYourServiceIsSupposedToDo2(), which might want to call flush() as well | |
amirabiri: guilhermeblanco: | |
guilhermeblanco: but that would be on a different EM | |
guilhermeblanco: so... it does the atomic job | |
guilhermeblanco: =) | |
amirabiri: no no read it again :-) | |
amirabiri: the potential problem is A -> B -> A | |
guilhermeblanco: amirabiri: that's why LOCK modes are there | |
guilhermeblanco: you do not execute a service call if it's in lock mode | |
guilhermeblanco: =) | |
amirabiri: OK that sounds like the missing piece... can you tell me a little more about that? | |
amirabiri: oh you mean we should implement that in our service objects? | |
guilhermeblanco: ya | |
amirabiri: BTW. How do you recommend implementing something like Spring's OpenSessionInView? | |
amirabiri: guilhermeblanco: ? | |
guilhermeblanco: I'm not Java experienced... so I have no idea what's the idea behind OpenSessionInView | |
guilhermeblanco: amirabiri: ^ | |
amirabiri: It's an AOP bit of handiness to let you wrap one web request in on Hibernate (actually one PM) session | |
amirabiri: Means you don't actually have to write it into your frontcontroller | |
amirabiri: I think we'll get round the problem by saying that services may not call other services | |
amirabiri: That kind of composition will be performed either by the controllers or by a facade | |
amirabiri: romanb: hi Roman can I drag you into this discussion? | |
guilhermeblanco: amirabiri: I think it should be allowed... | |
guilhermeblanco: if you have an IdentityMap | |
guilhermeblanco: you can keep track of your ServiceLayer calls | |
guilhermeblanco: and then you can add state to them | |
guilhermeblanco: this is similar to what we did earlier in Doctrine_Record of D1.X | |
guilhermeblanco: You can use the ServiceLayerManager | |
guilhermeblanco: which contains a SplObjectStorage or something similar | |
guilhermeblanco: that can be used as a IdMap | |
guilhermeblanco: you can use the service name as key | |
guilhermeblanco: and use the Factory/Manager to check its state before attempt to double flush | |
amirabiri: Actually this gets even more complicated because we have to distinguish between inter-services calls in the same php instance and calls between different instances. This is actually easier with php than java | |
amirabiri: since we don't have multi-threading going on and persistent (to the thread) caches | |
amirabiri: So I can see what you are talking about WRT IdMaps etc, but that's not really going to help with with our arch | |
guilhermeblanco: amirabiri: hm... I'm out of ideas then... but we can continue the discussion | |
guilhermeblanco: I'd then recommend you to make each ServiceLayer component to be atomic by itself, and not calling other Services | |
guilhermeblanco: but that is not good from arch POV | |
amirabiri: That's the same conclusion we've reached over here | |
amirabiri: Although that's very limiting and likely to bite us in the butt at some point | |
amirabiri: but I think it's the best option | |
amirabiri: I think a lot of this would be better if Doctrine alloed you to call flush($entity) | |
guilhermeblanco: amirabiri: I think this can be a good option also | |
guilhermeblanco: could you open a ticket related to that? | |
amirabiri: Sure :-) | |
guilhermeblanco: this would allow Entity Services that could be consumed by Service Layer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment