Conflict
Management and ASTA Providers
What Is A Conflict?
In a multi-user database system, a conflict occurs
whenever two or more users are editing the same data at the same time. For
example, the following situation would create a conflict:-
1.
User A edits a particular record but does not yet save changes.
2.
User B edits the same record.
3.
The users are now in conflict as one is editing non-current data.
The result of this action could be:-
4.
User A saves changes.
5.
User B saves changes.
User A’s changes have been lost because the values that
were saved by User B included the original data, before User A saved their
changes.
How Can You Avoid This Problem?
The solution to conflicts generally comes in the form of
a locking mechanism. This typically involves locking a record whenever a user
begins editing. In the above example, the action at number 2 would not have
been allowed.
Various database backends provide record locking, but
more commonly software developers will add a “LockUser” field to
particular tables, or maybe a “RecordLocks” table. These fields are
manually set by the application at the start of an edit and cleared after the
edit is applied or cancelled. While this approach is simple, there are a few
problems:-
·
A user’s system crashes and hence leaves a lock even though
the connection is lost. To overcome this an administrator function for unlock
records is required.
·
Concurrency restrictions. You can unnecessarily hinder
performance by making users wait for one another when this is not necessary.
This can have serious implications in busy systems, particularly if
table or page locking (as opposed to row locking) schemes are used.
·
There are extra queries on the SQL backend because each update
needs three queries, one to lock the record, one to do the update and one to
unlock the record.
How Can ASTA Providers Help?
ASTA Providers give another arguably better solution,
where the server “pushes” out changes that one user makes so all users see
those changes immediately. In the above example, action 4 would result in User
A’s changes being visible to User B and hence User B’s update would not
overwrite any data.
As of ASTA 2.5, providers are now capable of merging
other users changes without the need to write any code.
Each Asta Client Data Set (ACDS) registers with a
provider on the server to receive updates. In addition to this there are a few
other options listed under the ProviderBroadcast
property of each ACDS:-
·
BroadCastAction
– baNone. Ignore all broadcasts.
– baCache. Cache all broadcasts for applying later. This is commonly used as
a Mutex to stop broadcasts being
applied while another action is in progress.
– baAuto (Default). Automatically apply the broadcast, including merging in
changes. This setting will apply any outstanding cached broadcasts when set.
·
CacheBroadCastsWhenEdit
A problem arises when a broadcast is received and the actual ACDS is in
edit or insert mode. The problem is the broadcast cannot be applied because
moving to another row in the ACDS (the row that the broadcast relates to)
would post the current changes and move the current record – a very
confusing situation for the user.
To overcome this problem, set CacheBroadCastsWhenEdit
to be true (Default) and the ACDS will automatically set BroadCastAction
to be baCache at the start of the edit, and baAuto after the edit (remember
setting it back to baAuto will apply any outstanding broadcasts).
·
MergeEditRowBroadcasts
This property determines whether to merge broadcasts that relate to rows
that have been edited but not saved and is the basis for conflict management.
If this property is true (Default), all changes to rows that have been edited
(either currently being edited or in the pending updates list when UpdateMode
is umCached) will be merged in rather than simply copying each broadcast field
value. If false, broadcasts will just be applied as normal by copying each
field from the broadcast to the ACDS.
The difference between merging and simply applying the broadcast is:-
Merge – User A changes “CustomerName”,
User B changes “PhoneNumber” and both are not saved. When User A saves the
changes, only the “CustomerName” is updated on User B’s screen because
User B had not changed this – if User B had changed the “CustomerName”
field, the change would be ignored and User B’s value would be unaltered.
When merging in changes, the rule is simple – “If User B hasn’t changed
a field, merge in that field’s value from the broadcast. If User B has
changed the field, leave the changes intact”.
Normal Simple Apply – In the
above example, when User A saves the changes, both the “CustomerName” and
“PhoneNumber” are updated on User B’s screen. This means there is no
conflict because User B sees the current data but is extremely annoying to
User B because their “PhoneNumber” changes are lost.
·
ProviderFilter
The filter entered here will be applied at the server to determine if a
particular broadcast should be sent to this client. Typically, this feature is
used when a number of clients all share the one provider but are only
interested in certain changes (eg only for the customer they have opened, but
there is only one customer provider on the server). The format of this
expression is that same as the Filter
property.
This is not related to conflict management as such but is mentioned as a
useful companion feature.
Customising How It All Works (Events)
Events are provided at various stages of the conflict
management process to allow customisation. Generally, you will probably not
need any of these events and will not have to write any code.
Should you need them, the main events are:-
·
ProviderBroadCastEditRow
This event will be fired for each row that is being merged. It allows the
software developer to make a custom screen of what is changed and to ask the
user which changes they would like to apply.
Parameters to this event are:-
DataSet D – This is the merge,
containing fields “Field” (the name of the field), “OldValue”, “NewValue”,
“EditChanged” (true if the user has changed this field) and “Apply”
(true for all fields that you want ASTA to merge in).
The dataset has one row for each field in the ACDS that is changed in the
broadcast and by default “Apply” is set to true when “EditChanged” is
false. You can display this dataset via the normal Delphi / BCB database
controls although note that “Field”, “OldValue” and “NewValue” are
BLOB field due to the completely variable nature and length of their contents.
Example of Contents of D:-
|
Field
|
OldValue
|
NewValue
|
EditChanged
|
Apply
|
|
“CustomerName”
|
“OldPerson”
|
“NewPerson”
|
false
|
true
|
|
“PhoneNumber”
|
“OldNumber”
|
“NewNumber”
|
true
|
false
|
Additionally, if you wish to handle the broadcast
yourself, set the Handled parameter to true and if you want to keep the merge
dataset (D) for some other purpose set DisposeOfDataSet to false.
·
ProviderBroadCastDeleteEditRow
A special case applies when User B deletes a row currently being edited by
User A. This event will fire for each row that is being deleted when there are
pending updates for that same row.
NOTE That at the point this event is fired, the row has physically been
deleted from the database. If User A wants to keep the record, you can update
the OldValuesDataSet and change the “AstaChange” field from dtEdit to
dtAppend but this should only be done by experienced users. If done, this
means that the changes will be kept by adding a new row with the old data but
and important note is that if there are any auto increment type fields, these
will get new numbers.
The default ASTA action if you don’t do anything is to delete the edited row
from the ACDS. If you handle the deletion yourself, set Handled
to true. The dataset D that is passed in, has its current row as the broadcast
for the deleted row.
This event will fire immediately when the broadcast is received (if BroadCastAction=baAuto)
and will ignore the CacheBroadCastsWhenEdit
property.
Other events that may be useful, but not directly related
to conflict management are (refer to online help for complete description):-
·
ProviderBroadCastBeforeApplyRow
This event is fired before each broadcast row is applied. A Handled
parameter is provided if you want to take specific action and not have ASTA
take its default action.
·
ProviderBroadCastBeforeApplyRow
This event is fired after each broadcast row is applied, and can be useful
for updating non-databound controls. Note that this event is only fired after
the actual broadcast is applied, so if BroadCastAction=baCache this will not
fire until later.
·
ProviderBroadCast
This is the standard provider broadcast event that has been present in
ASTA for some time. You can set this event to completely take control of the
broadcast functionality.
Summary
Remember, that if you don’t want to do anything
special, no code is required. ASTA will happily apply the default logic with
just a couple of property settings.
To enable conflict management via providers:-
·
Register the ACDS for updates.
·
BroadcastAction=baAuto (Default)
·
CacheBroadcastsWhenEdit=true (Default)
·
MergeEditRowBroadCasts=true (Default)
The rest is done for you!
Conflict management can be a complicated issue, take time
to consider the theory behind what is being internally done by ASTA. This
provider “push” technology is just one option to handle conflicts - record
locking or other schemes are still worth considering and can be better in some
situations.
You should discuss record locking issues with your
clients together with associated costs. It is not uncommon to be formally told
by a client to ignore conflicts and hence not incur extra costs (the extra
costs can be significant when development and testing is taken into
consideration). This is of course decided on a project by project basis.