-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow to not Compare Object Types by reference #5542
Comments
I proposed comparing the DB-side representation in the past, but it may be
|
@Ocramius maybe we can enable this comparison based on a setting, so its only done for fields where its necessary. |
I expect that if someone has something like a How about making it so that an Then |
Possibly, but I'd still look at it after @guilhermeblanco is done with digging through JPA's field mappings.
The idea is to generate specific comparators per entity (you pretty much always compare entity to entity, not just single fields). |
I interpreted @beberlei as describing a different scenario, where there is only one Doctrine Entity, and someone has used a custom DBAL mapping type for a specific field on it. Then the issue is with how the ORM does dirty-checking on each of the fields. For example, suppose someone creates a
The underlying behavior is like:
But for that particular class of field-representing-object, the designer might want custom behavior more like:
|
Probably affects objects serialization: http://stackoverflow.com/questions/30193351/how-to-update-doctrine-object-type-field |
I have a couple custom types, and it a bit annoying 😄 , so I got 2 ideas:
Maybe can just add one method to the // Default
public function compareValues($old, $new){
return $old === $new;
}
// For DateTime
public function compareValues($old, $new){
return (string) $old === (string) $new;
} then UOW just do Is it expensive?
$isDifferent = is_object($old) && method_exists($old, '__toString')
? (string) $old === (string) $new
: $old === $new;
// Of course need to check that both $new and $old is not empty Which is less expensive? The first one, is more flexible, and gives some freedom for developers, but require changes in both DBAL and ORM. Second one is more simple (can be done only in ORM side), but it also less flexible. |
Both are extremely expensive, since an additional set of method calls per field per value per stored UoW change is to be performed. This is going to massively affect ORM performance, and can only be done if the checks can be compiled into the UoW. |
okay, more complicated than I thought
yeah that can be more expansive, because hm, okay, not perfect |
I have made tests. default
|
Hey Fedir,
Please use phpbench when writing tests, as it considers standard deviation
and other factors.
I see a 23% slower API there - that's not gonna be OK.
Marco Pivetta
http://twitter.com/Ocramius
http://ocramius.github.com/
…On Wed, Nov 8, 2017 at 9:15 PM, Fedir Zinchuk ***@***.***> wrote:
I have made tests.
The full source of the test is there https://github.com/Fedik/
doctrine-changeset-test
*(all time in seconds)*
default
Dummies 10000 items
Make dummies: 3.6029109954834
Compute changes: 0.1790030002594
Recompute changes: 0.22904109954834 <02290%204109954834>
method_exists($orgValue, '__toString')
Dummies 10000 items
Make dummies: 3.6226229667664
Compute changes: 0.18751001358032
Recompute changes: 0.23972296714783 <02397%202296714783>
*note: DateTime does not have __toString so it ignored here*
Doctrine\DBAL\Types\Type::isValuesIdentical($val1, $val2)
Dummies 10000 items
Make dummies: 3.6306829452515
Compute changes: 0.22055912017822 <02205%205912017822>
Recompute changes: 0.24937987327576 <02493%207987327576>
The simple objects like Point do not make huge difference, it around the
same with default.
But more complex Objects can take some more time, that true.
From my point of view Doctrine\DBAL\Types\Type::isValuesIdentical($val1,
$val2) is good enough to accept.
if I have missed something in my test, please tell me.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5542 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAJakBufPuBWKlnXk1arnQ6ziGktAqBYks5s0gvegaJpZM4Gym55>
.
|
Well, this an interesting ticket from the past. Looking at my previous post, am I correct in assuming that the use-case we're all talking here about involves checking the dirty/changed status for custom DBAL types that represent a column? In other words, the same problem causing the warning that "DateTime changes are detected by Reference"? @Ocramius I'm not sure that the performance outlook is all that dire, because in practice the ORM can skip the method call for a majority of entity-properties. Suppose there's are some new potential methods on
The output from the first method can easily be cached, so that most cases (ex: To sketch it out:
So ~95% of the time the only additional cost is checking a boolean value, one that is effectively a constant for the lifetime of the process. The other 5% of the time, like when using P.S.: Originally I got stuck thinking of this as an equality test, but it really isn't, because both arguments to |
@Fedik My concern about
And later on the |
Note: Issue with mutability also applies to Embeddables, but on entirely different level of implementation. If there's to be some kind of a Comparison API, it'd be good to share the core idea between Types and Embeddables. |
Any movement on this issue? Has any decision on the way forward been made? |
@surelygroup nothing moving for now. |
DHager's proposed solution in #5542 (comment) would solve our use-case too. And from a performance point of view it seems it could be reasonable. While adding an extra check on each field will cost something, we should not forget the cost of the SQL query itself. In our use-case we have custom Point type. All entities with a field of that type will always execute and The fact that there is always a changeset also happen to trip up our "last updated" mechanism. An entity with with a custom field of Point will always be marked as last updated "now", even if nothing actually changed. I guess there could be workarounds for that particular issue (by manually re-checking the changeset for actual changes), but it seems to me that Doctrine should not notify us of change if nothing is actually changed. @Ocramius would you accept a PR (actually two, for DBAL and ORM) that implements DHager's solution ? |
For future reference, this problem, or a variation of it, came up a few times over the past 10 years: #3550, #7583, #7586, #7892, symfony/symfony#11732 |
As I understand this problem has no easy, one size fits them all solution, for people like me who can live with a workaround, is this one a good enough ? add the comparison in the setter, i.e for mutable objects (like datetime or any custom type) having
Or is there some special magic in doctrine, that even calling the setter would marks it as dirty ? If not and it seems good enough, I can start evangelizing this work around in my company's codebase (and even change the Of course one can still do $this->getStartingTime()->modify() but this can be check by static analysis like phpstan with phpstan-doctrine |
If your real use-case is indeed |
I have also the case with column of type "decimal" which seems to also be represented internally by an object, for DateTimeImmutable, it only fix the issue of doing "->modify". |
DBAL decimal type is not an object, but a string, so you should not have the same issue with that type. |
no , my issue is spefically that putting the same point in time , create two objects (especially if it is immutable), even if both are "first january of 2022 at midnight" , causing unnecessary
weird because that's the other type causing this issue on our side, could it be due to formating i,e we set "5" and doctrine has "5.00" ? so they are no === even though they are the same fixed point number ? |
Sorry I got it all backwards. Of course you are right, DateTimeImmutable would not solve your issue. |
This issue currently has a broad scope, which might not help in resolving it. Maybe perfect is the enemy of good here: Fixing all objects and providing some hooks arch etc is hard, so maybe it would be a start to fix native PHP object behavior first. |
Send a patch: @DHager's analysis on happy path scenario makes sense, but it needs practical implementation and benchmarks (committed). |
There are currently many Use-cases where the data retrieved from the database is treated as an object,
using DateTimeImmutable or Value Object can solve some problems, but it always require extra code. I started a PR (#10137) which proposes to override the behavior with this interface:
It can also be a starting point for the proposition of managing this in the Doctrine DBAL type:
There are no breaking change with this solution:
Does this seem a good solution and should I continue this PR? |
I like the proposal from @arno14 as it looks like a reasonable way to implement something like that from a user's perspective.
Oh yes, I also wasted some hours scratching my head what was wrong with my implementation, as my previous immutable siblings worked just fine before. I have a lot of serialized JSON custom types and most of them are immutable, but some are mutable and those already implement an internal dirty state that an Interface could easily expose. For now I do something like this as a workaround, if anyone is looking for a quick solution unless that underlying problem is eventually solved in any way: public function addCustomTypeEntry(CustomTypeEntry $customTypeEntry): self
{
// where $this->customTypeProperty is a mutable object holding a list of CustomTypeEntry objects serialized as JSON
$this->customTypeProperty = (clone $this->customTypeProperty)->addEntry($customTypeEntry);
return $this;
} |
@Ocramius does the newest versions of dbal and orm make it easier to implement? |
No: conversions still very expensive. |
Some times ago, I have been working on this issue, On this branch: After reflexion, I realized that any process done if the ChangeDetector would be more or less So I also created this branch : https://github.com/arno14/doctrine-orm/tree/5542_compare_value_by_database_value But it is clearly not optimized because the process is that :
When calling Flush :
So Type::convertToPhpValue and Type::convertToDatabaseValue are executed twice for the same datas So to resolve this very old issue, I think it would be necessary to rethink the way original datas are stored, When fetching an entity :
When calling Flush :
But with this solution, the following problems would occurs:
The problem of the BC break could be minimized by adopting a new FieldMapping option "detectChangeByDatabaseValue". |
If we have a Doctrine DBAL type with an object (DateTime, any custom object), then its compared by reference and we cannot use mutable objects.
We should consider allowing a way to have mutable objects here and delegate the changeset compuutation to a service or do something clever.
The text was updated successfully, but these errors were encountered: