Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

"Set refresh to" and Tableupdate() in multi-user environment 1

Status
Not open for further replies.

markftwain

Technical User
Jul 12, 2006
108
Hi all, (this goes to understanding)

Please help with clarification on: "set refresh to 0,n"

If n >0, in a multi-user environment, does "ram" get updated regardless of the buffering scheme being used?

Is it possible that if N >0 for user1 and user2 has completed a write to disk on the same data user1 is editing (buffermode >1), will tableupdate() for user1 see what already exists in user1's ram for that record or will tableupdate() first grab what is actually on the disk (from user2) before attempting the update?

That is, if N=0, what does tableupdate() actually use for the disk value--that which was previously read into the ram (and is now different from the disk), or what is actually on the disk?

Thanks again for your help
 
I'm trying to figure out where this question comes from and what it means. :)

From my copy of the help file:

SET REFRESH affects records displayed in a browse window opened with the BROWSE, CHANGE, or EDIT commands. Memo fields opened for editing in a browse window, for example, using the MODIFY MEMO command, are also updated.

Are you worried that your browse windows won't contain the right value?

I suspect this ties in with your other post about record locking. Locking a record always refreshes the local buffer no matter what. You cannot update a record (either directly or via tableupdate()) without a lock (either implicit or explicit).
 
Hi,

From Hacker's Guide to VFP by T. E. Granor and T. Roche

A BROWSE shows a number of records on the screen at any one time, but other users might be changing those records as you watch. If REFRESH is left at its default state of 0,5, these changes are never displayed. For table fields displayed with other controls, you must lock a record to be sure the fields display the actual values stored on disk, but REFRESH can "read through locks" to show you the most up-to-date information available while within a BROWSE.

We generally feel that BROWSEs should not be used in applications; instead, they should be replaced by the far more controllable Grid. See "Commands Never to Use" for more details. Grids don't seem to be affected by the setting of REFRESH, requiring either that the row receive the focus, the form's Activate event occur, or the form's Refresh method be called in order for data to be properly displayed.

The second parameter is less well documented. This parameter supposedly determines how often the locally cached data buffers should be updated with new data. Don't ever set this parameter to zero, or you won't see changes made to records displayed on screen until the records are locked.

It seems to us that the default setting of 5 seconds should beat the server to death with so many requests to update data that the entire system would come crashing down, but that hasn't been our experience. On very large systems or WANs where network bandwidth is a precious commodity, we recommend you try raising the refresh level by doubling it and checking the performance change to the network and application. Ten, 20 or even 40 seconds between updates might not make that much difference in the appearance of your application, but could significantly affect other network performance criteria. As always, your mileage may vary; test your application under your environment to be sure of optimal performance.

Example SET REFRESH TO 30

hth

Mark

 
I try to answer this from the perspective of the buffering technology. Buffers are private per workarea and not written anywhere else before they are comitted to the dbf.

Therefore a refresh alone will never update user1 with what user2 has not yet written to the dbf. Also once a record is buffered the buffer will not be updated, even not by changes already written to the dbf.

Another perspective: cursoradapter requery or requerying a view cursor only is possible, once the cursor buffer is cleared either by comitting it via tableupdate or dicarded by tablerevert(), and then you can refresh a ca or view cursor with data from the dbf.

All this is independant on the refresh setting.

Now taking a look into the help on SET REFRSH: The functionality is not limited to browse windows and their satellite memo edit windows. Next sentecne in the help is:

vfp help said:
Visual FoxPro buffers portions of tables in memory on your workstation. SET REFRESH can specify how often data that is buffered locally on your workstation is refreshed.[/quote vfp help]

Also the first sentence in the help says:
vfp help said:
Determines whether to and how frequently to update a browse ... or to refresh local memory buffers with changes from other users on the network.[/quote vfp help]

But this does not mean the buffered changes, but the not yet touched records only. Eg user1 and 2 open the table and table burffering is set on. user1 changes record1 and record2, user2 edits record2 and record3, user 2 saves, all before the next refresh cycle, if the refresh does update user1, user1 will see the change in record3, but not yet changes of record2, as he also has began editing this.

We're having a two staged buffering. Buffer of the read in values (rather caching buffer), buffers of edits (write buffer) and then there is the dbf and what's currently written to disc.

When you brows, you think of it as a live view of the dbf file, but in fact it can't be, can it? It's open shared, all users users only see the read in values and this is updated with the refresh mechanism, if you like, or by requerying actively, also indirect by scrolling in the browse. So without having talked about the buffering mechanism accumulating all uncommitted changes of a user, we already have two stages: dbf and what's in memory displaying in the browse. And only up to that level refresh works.

To illustrate you can use this code:
Code:
Close Tables All
* everything is buffered optimistical:
CursorSetProp("Buffering",5,0)
* refresh after 10 seconds
Set Refresh To 10


Cd GetEnv("Temp")
Erase tabTest.*
Create Table tabTest (cText C(5))
Insert into tabTest Values ("this")
Insert into tabTest Values ("is a")
Insert into tabTest Values ("test")
Use

* user1 and 2 open the table:
Use tabTest in 0 Shared Alias user1
Use tabTest in 0 Shared Again Alias user2

*this is what they see
Select user1
Go top
Browse nowait name oBrowse1
oBrowse1.Top = 0

Select user2
Go top
Browse nowait name oBrowse2
oBrowse2.Top = 200

* user1 changes rec1+2
Update user1 set cText = Left(cText,4)+'1' where Recno()<3
Go top in user1
* user2 changes rec2+3
Update user2 set cText = Left(cText,4)+'2' where Recno()>1
Go top in user2

Save this to a prg and run it.
You see user1 and user2 only see their own changes, because nobody saved changes yet.
Now activate the command window and there manually do:
Code:
TableUpdate(.T.,.F.,"user2")

Don't touch anything. After about the refresh time of 10 seconds, the User1 browse will be refreshed.
Record 3 then is "test2" in user1, but the change of record2 is not visible, as user1 also is already changed from
the original value.

Nevertheless: I recommend to never use the refresh mechanism. Let users requery the data before editing, which is automatically done by setting refresh to 0 (always read from disk). This way the moment user1 changes to record 3 he will update that with what's on disk. He
will not see changes before navigating there, but just in time before editing a record he navigate to, so his own editing will always start with what's in the dbf.

He will only see the conflict of editing record1 to "is a1" instead of "ia a2", when he saves his own changes, but you can see the conflict by inspecting user1.cText vs. Oldval() and Curval(): eg in the command window now do:

Code:
Select user1
Goto record 2
? Oldval('cText')
? user1.ctext
? Curval('cText')

So while the user1 browse window shows "is a1", you can determine the initial value (oldval - the old value as it was before editing), thereby detectig user1 changed this field from the old value to a new one. And you can see the changed value in the dbf (curval - the current value on disc in the dbf file), thereby you can also see another user changed this value from oldval to curval. As these all differ you can see the conflict, before it would cause tableupdate() to return .f.

So while user1 will not automatically see user2 has edited the same record, you can see it via code you could put into a conflict check routine before using tableupdate(). You also decide to use tableupdate() with the lForce parameter set .T., which means user1 would overwrite user2 changes and finally in the dbf record2 would become "is a1".

You have all possibilities at hand you need without using the refresh mechanism.

Bye, Olaf.
 
Thanks everybody for the great answers.

This question comes after the following observations. With "set refresh to 0,0", in a multiuser environment, a grid populated by a view (from table1) and a form with text controlsource's tied directly to a second buffered table (table2) were simultaneously showing different values between two seperate users even though both user1 and user2 had saved their changes.

But changing to "set refresh 0,10", without any other program changes, correctly showed user2 and user1 the changes made by the other.

There are no browse windows, but there is a routine to check field changes when a tableupdate() shows a record conflict. Hence the question of oldval() showing the actual table versus what was placed in ram on the last refresh cycle.

Anyway, you all are correct. ( Yes Dan, you are correct. Thank you Mark, your experience mirrors mine exactly. Thanks Olaf for the program demonstration.)

Bye, Mark
 
It's curval() showing what's in the dbf currently, not oldval().
And it's obvious from the help set refersh 0,0 does not refresh anything.

I'd recommend set refresh 0,-1. Yes, that means users will not see each others changes by waiting only. But the moment you click into a textbox you'd get an update from the dbf. So you see what you edit before editing.

See here a modification of my sample with a form. Now user1 is commiting his changes and the form is what user 2 sees.

With a setting of refresh 0,-1 the textbox does not change to the actual value in the dbf. It displays 'this', no matter how long you wait. But once you tab or click into it, it will display the current value in the dbf 'this 1', as changed from user1.

This is having the advantage of seeing other users changes without polling data. I can only say from an enterprise application with 100s of concurrent users in an even larger network of thousands of workstations, any refresh setting, where all station poll data is bad for the LAN.

Code:
Close Tables All
* everything is buffered optimistical:
CursorSetProp("Buffering",5,0)
Set Refresh To 0,-1


Cd GetEnv("Temp")
Erase tabTest.*
Create Table tabTest (iid I,cText C(5))
Insert into tabTest Values (1,"this")
Insert into tabTest Values (2,"is a")
Insert into tabTest Values (3,"test")
Use

* user1 and 2 open the table:
Use tabTest in 0 Shared Alias user1
Use tabTest in 0 Shared Again Alias user2

*this is what user 2 sees
oForm = CreateObject("aForm")
oForm.show()

* user1 changes rec1
Update user1 set cText = Left(cText,4)+'1' where iid=1
TableUpdate(.T.,.F.,'user1')

Read events

Define Class aForm As Form
Add Object text1 as textbox with;
  top=10,;
  left=10,;
  controlsource = 'user2.iid'

Add Object text2 as textbox with;
  top=40,;
  left=10,;
  controlsource = 'user2.cText'

Add Object cmd1 as commandbutton with;
  top=70,;
  left=10,;
  caption = 'Exit'

  Procedure cmd1.click()
    thisform.Release()
  EndProc
  
  Procedure unload()
     Clear Events
  EndProc
Enddefine

Bye, Olaf.

PS: the main intention of the iid field is that the form has more than the single textbox bound to cText, otherwise you'd instantly see the dbf value anyway and not see the effect refresh 0,-1 has.
 
Thanks Olaf,

For what its worth, I am using MS Remote Desktop/Application technology (MS did a great job with it) so as to avoid vfp from "sending" the "files" anywhere off of the server and manage broken connections. So if vfp is "moving any files/data" around, it can't be far. I'm guessing, "set refresh" can't be to bad since nothing leaves the machine.

Bye, Mike
 
Hi Mike,

running at a dedicated server and running via remote desktop surely makes refresh a local thing. Nevertheless you don't gain much, refresh also only propagetes what's been written to the dbf, refresh 0,-1 does the same just in time, just if needed.

Bye, Olaf
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top