Skip to content

Instantly share code, notes, and snippets.

@doctrinebot
Created December 13, 2015 18:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save doctrinebot/33cf72b426f1a3d70421 to your computer and use it in GitHub Desktop.
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
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