View Issue Details

IDProjectCategoryView StatusLast Update
423PHPOF2Defaultpublic26 Mar 2013 12:55
Reporteriarmeanu Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status confirmedResolutionopen 
Product Version0.12.3 
Summary423: update() overrides fields that were not meant to be updated
DescriptionWhen calling update() on an object, the current entire state of that object is persisted, rather than only the fields that changed. This may be a big problem if two concurrent processes write on the same record.

I suggest caching the properties of the object as it was when it was red last time ( which we probably do already for audit purposes ), and when update() is required/requested, only update the fields that have changed since the last read.
Steps To ReproduceConsider the following scenario:

Webpage 1 needs to modify only last_access_time, and webpage 2 needs to modify only only access_number ( on the same record )

-------------------
time is : 10:00:01

webpage 1 reads the record with two properties:
  - accessed_number: 7
  - last_access_time = 10:00:01

webpage 2 reads the same record with two properties:
  - accessed_number: 7
  - last_access_time = 10:00:01

--------------------------------
time is 10:00:02

webpage 1 only changes:
  - last access_time = 10:00:02
  and calls update()

webpage 2 only changes:
  - accessed_number = 8
  and calls update()

--------------------------------
time is 10:00:03

the record shows:
  - last access_time = 10:00:01
  - accessed_number = 8

overriding the change made by webpage 1, because when webpage 2 called update() the last_access_property was reverted back to what it was when webpage 2 first red it ( 10:00:01 )
TagsNo tags attached.

Activities

timj

25 Mar 2013 22:59

manager   ~466

Agreed that this would be a good thing. In fact there is a TODO in the code about exactly this, so would definitely accept a patch.

Two points:

1. note that this would increase memory usage/marginally slow down PHPOF, as it would need to save values on *every* row retrieval (at the moment it only does if auditing is enabled/DBRow::$record_changes==true). This would need to be measured.

2. it does not actually solve the stated problem, only makes it slightly less acute. It is still the case that two concurrent changes will result in a "last wins" situation. In fact, it can be argued that in some cases (probably a minority) it will end up with a worse result, because you could end up with a state which is not consistent with the values submitted by either process/user.
Without storing full state, the only real way of properly addressing this is something like:
a) in each table, have an extra column with last changed time (or use the audit log)
b) when retrieving data, cache the last modified time of that row
c) if - on submission - the row has been changed by another process in the meantime, if there are any fields that conflict then re-prompt the user to submit (assuming it's a user submitting a form)

This is roughly how it's done in Bugzilla (http://bugzilla.org) to good effect.

iarmeanu

26 Mar 2013 12:55

reporter   ~469

It will solve the stated problem ( because the two pages update different properties on the record ).

It will not solve the problem of two processes that write the same property concurrently ( but that should be addressed by using transactions with various locks set for the record - a fix for bug 422 ).

Issue History

Date Modified Username Field Change
25 Mar 2013 14:08 iarmeanu New Issue
25 Mar 2013 22:59 timj Note Added: 466
25 Mar 2013 22:59 timj Severity major => minor
25 Mar 2013 22:59 timj Status new => confirmed
25 Mar 2013 22:59 timj Product Version => 0.12.3
26 Mar 2013 12:55 iarmeanu Note Added: 469