Managing Conflicts When Updating Data
Whether you choose buffering, transactions, or views, you must manage conflicts during the update process. Managing conflicts encountered in multiuser environments can require extensive and repetitive code. A complete conflict management routine performs the following:
Detects a conflict.
Identifies the nature and location of the conflict.
Provides enough information so that the user can intelligently resolve the conflict.
For an example of a conflict management routine, see the data checker class in Samples.vcx, located in the Visual FoxPro ...\Samples\Classes directory. Just add the class to a form and call the CheckConflicts method before any operation that writes buffered data to the table, for example moving the record pointer if you're using row buffering, closing a table, or issuing TABLEUPDATE( ).
Managing Buffering Conflicts
You can make data update operations more efficient by carefully choosing how and when to open, buffer, and lock data in a multiuser environment. You should limit the time a record or table is subject to access conflicts. Still, you must anticipate and manage the inevitable conflicts that result. A conflict occurs when one user tries to lock a record or table that's currently locked by another user. Two users cannot lock the same record or table at the same time.
Your application should contain a routine to manage these conflicts. If your application doesn't have a conflict routine, the system can lock up. A deadlock occurs when one user has locked a record or a table and tries to lock another record that's locked by a second user who, in turn, is trying to lock the record that's locked by the first user. While such occurrences are rare, the longer that a record or table is locked, the greater the chance of deadlock.
Trapping Errors
Designing a multiuser application or adding network support to a single-user system requires that you deal with collisions and trap for errors. Using Visual FoxPro record and table buffers simplifies some of this work.
If you attempt to lock a record or table already locked by another user, Visual FoxPro returns an error message. You can use SET REPROCESS to automatically deal with unsuccessful lock attempts. This command, in combination with an ON ERROR routine and the RETRY command, enables you to continue or cancel the lock attempts.
The following example demonstrates automatic reprocessing of a failed operation, using SET REPROCESS.
Using SET REPROCESS and ON ERROR to Manage User Collisions
Code |
Comment |
---|---|
ON ERROR DO err_fix WITH ERROR(),MESSAGE() SET EXCLUSIVE OFF SET REPROCESS TO AUTOMATIC USE customer IF !FILE('cus_copy.dbf') COPY TO cus_copy ENDIF |
This routine runs if an error occurs. Open the files non-exclusively. Reprocessing of unsuccessful locks is automatic. Open the table. Create the APPEND FROM table if needed. |
DO app_blank DO rep_next DO rep_all DO rep_curr DO add_recs |
The main routine starts here. These commands are examples of codes that could be executed in the course of your program. |
ON ERROR |
The main routine ends here. |
PROCEDURE app_blank APPEND BLANK RETURN ENDPROC |
Routine to append a blank record. |
PROCEDURE rep_next REPLACE NEXT 1 contact WITH ; PROPER(contact) RETURN ENDPROC |
Routine to replace data in the current record. |
PROCEDURE rep_all REPLACE ALL contact WITH ; PROPER(contact) GO TOP RETURN ENDPROC |
Routine to replace data in all records. |
PROCEDURE rep_curr REPLACE contact WITH PROPER(contact) RETURN ENDPROC |
Routine to replace data in the current record. |
PROCEDURE add_recs APPEND FROM cus_copy RETURN ENDPROC |
Routine to append records from another file. |
The following example demonstrates an error procedure that starts when the user presses ESC.
Error Handling Using the ESC Key
Code |
Comment |
---|---|
PROCEDURE err_fix PARAMETERS errnum, msg |
This program is called when an error is encountered and the user escapes from the wait process. |
DO CASE |
Figure out what kind of error this is. Is it "File is in use by another"? |
CASE errnum = 108 line1 = "File cannot be locked." line2 = "Try again later..." |
|
CASE errnum = 109 .OR. errnum = 130 line1 = "Record cannot be locked." line2 = "Try again later." |
Or "Record is in use by another"? |
OTHERWISE line1 = msg + " " line2 = ; "See your system administrator." ENDCASE |
Or is it unknown? |
=MESSAGEBOX( line1 + line2, 48, "Error!" ) RETURN |
Display the error message in a dialog box with an exclamation point and an OK button. |