update 50-10-20
ตอนที่ทดลองนี้ cifs บนเดเบียน sid (samba-3.0.26a-1) แก้ปัญหาเรื่องแคชไม่ตรง และล๊อกเรคคอร์ดช้าได้แล้ว
โดยการแก้ไขไฟล์ smb.ini ในหมวด share ให้มีพารามิเตอร์คือ
[data] ... strict locking = yes oplocks = yes level2 oplocks = no ...
และเมานต์ด้วยพารามิเตอร์ directio เช่น
$ sudo mount -t cifs //server1/data /mnt/data -o username=USER,password=PASSWORD,iocharset=utf8,directio
จะสามารถล๊อกเรคคอร์ดได้ตรงและแก้ปัญหาแคชไฟล์ได้แล้วครับ
เป็นแค่การทดลองเท่านั้น
ถ้าเครื่องลูกข่ายของ foxpro เป็นวินโดวส์ล้วน ไม่ต้องใช้ฟังก์ชั่นในบล๊อกอันนี้ ซึ่งเป็นการเขียนฟังก์ชั่นเพื่อแก้ปัญหาการล๊อกเรคคอร์ดสำหรับลูกข่ายที่เป็นลินุกซ์เท่านั้น
เนื่องจากการเมานต์แบบ cifs มีปัญหากับแคช ทำให้การอ่านค่าเรคคอร์ดในไฟล์ไม่ตรง จึงต้องเลือกใช้การเมานต์แบบ smbfs แทน ซึ่งการเมานต์แบบ smbfs มีข้อเสียอย่างแรงคือ ไม่สามารถล๊อกไฟล์และเรคคอร์ดได้ จึงทดลองเขียนฟังก์ชั่นการล๊อกไฟล์ขึ้นมาใช้เอง
(ยังค้นคำตอบสำหรับการนี้ในกูเกิลไม่พบ - พบแต่คำถาม ไม่พบคำตอบ ส่วนใหญ่ย้ายไปใช้ระบบฐานข้อมูลกันหมด)
วิธีการคือสร้างไฟล์ข้อมูล dbf ขึ้นมาเก็บค่า ตาราง, เลขที่เรคคอร์ด, เครื่องที่ทำการล๊อก, และเวลาตอนล๊อก
เวลาต้องการล๊อกไฟล์และเรคคอร์ด ก็ให้มาเขียนลงในไฟล์นี้ทุกครั้ง และตอนปลดล๊อกก็ลบออกทุกครั้งเช่นกัน
โดยเวลาเรียกใช้สำหรับการล๊อกไฟล์ ก็เรียกด้วยฟังก์ชั่น =fxflock()
และการล๊อกเรคคอร์ดก็เรียกด้วยฟังก์ชั่น =fxrlock()
จากการทดลองพบว่า ถึงแม้แคชของ smbfs จะเร็วกว่า cifs มากก็ตาม แต่ก็ยังไม่ใช่แบบทันทีทันใด
เราจะแก้ปัญหาแคชด้วยการใช้คำสั่ง foxpro ว่า go recno()
เพื่ออัปเดตแคช
และพบว่าถ้าเรามีการ เปลี่ยนแปลงข้อมูลในเรคคอร์ด จะทำให้การอัปเดตรวดเร็วขึ้น จึงใช้เทคนิกอันนี้ในการรีเฟรช โดยทุกครั้งที่เกิดการรีเฟรช จะเขียนชื่อเครื่อง (gcMachineName
) ลงไปที่เรคคอร์ดแรกของไฟล์ lockhndl
ถึงแม้จะแก้ปัญหาได้ดีพอควรก็ตาม แต่ยังไม่พอสำหรับการล๊อกไฟล์ ซึ่งเราต้องปล่อยให้เร็วที่สุด
ดังนั้นสำหรับการล๊อกไฟล์ จะกันเหนียวอีกชั้นนึงด้วยการเขียนลงไฟล์สำหรับการล๊อค (ข้อมูลว่าง ๆ ) มาช่วยอีกแรงนึง จึงสามารถล๊อกและปล่อยล๊อกได้แบบทันทีทันใด
เนื่องจากมีการใช้ข้อมูลในการล๊อกเป็นชื่อเครื่องด้วย ซึ่งปรกติเราเรียกผ่านฟังก์ชั่น foxpro คือ sys(0)
แต่พบว่าเครื่องลูกข่ายที่เป็นลินุกซ์ (+dosemu+freedos) ไม่มีข้อมูลตัวนี้ เราจึงต้องกำหนดข้อมูลตัวนี้ขึ้นมาเองในทุก ๆ เครื่อง ซึ่งในโค๊ดจะปรากฎในชื่อตัวแปร gcMachineName
ส่วนโค๊ด
function fJustFName && RETURN JUST FILENAME parameter cFile private cLoc,cSep cLoc=locfile(cFile) cSep=iif('/'$cLoc,'/','\') return right(cLoc,len(cLoc)-rat(cSep,cLoc)) function fJustPath && RETURN JUST FILENAME parameter cFile,lIsIncludeSep private cLoc,cSep cLoc=locfile(cFile) cSep=iif('/'$cLoc,'/','\') if lIsIncludeSep return left(cLoc,rat(cSep,cLoc)) else return left(cLoc,rat(cSep,cLoc)-1) endif return function fJustStem && RETURN STEM OF FILENAME parameter cFile private cName cName=fJustFName(cFile) return left(cName,rat('.',cName)-1) function fxflock && RETURN FLOCK STATUS * SOLVED LOCKED FAIL IN LINUX * BY CREATE NEW LOCKED TABLE TO HANDLE LOCKED STATE * FLOCK([<expN> | <expC>]) parameter xArea return fxSubLock(xArea, 0) function fxrlock && RETURN RLOCK STATUS * SOLVED LOCKED FAIL IN LINUX * BY CREATE NEW LOCKED TABLE TO HANDLE LOCKED STATE * RLOCK([<expN> | <expC1>] parameter xArea return fxSubLock(xArea, -1) function fxFlushLock parameter cFile use (cFile) go 1 && RESERVE FIRST RECORD FOR FLUSH DATA replace ftable with gcMachineName go bott flush go 1 list nocon use flush return function fxSubLock parameter xArea, nRecno private cTable,nLoop nLoop=50 && TRY TO LOCK FOR 50 TIMES cLockHndl='lockhndl' =fxCrLockHndl() nOldArea=sele() xTemp=type('xArea') if xTemp='N' .or. xTemp='C' sele (xArea) else if len(dbf())=0 return .F. endif endif if nRecno=-1 nRecno=recno() endif cTable=fjuststem(dbf()) ***** if nRecno=0 do while file(cTable+'.LCK') .and. nLoop>0 wait wind "Wait file lock "+cTable timeout .1 enddo if nLoop=0 return .F. endif nloop = 50 endif if nRecno=0 cSafe = set('safe') set safe off disp to file (cTable+'.LCK') noconsole set safe &cSafe endif ***** sele 0 =fxFlushLock(cLockHndl) use (cLockHndl) locate for fTable=cTable .and. (fRecno=nRecno .or. fRecno=0) .and. fMachine!=gcMachinename do while found() .and. nLoop > 0 *wait wind "Wait for unlock..." timeout 1 wait wind str(nloop,3)+" wait "+ftable+str(frecno,4) timeout .1 *wait wind "Wait for unlock..." nowait go recno() go bott go top locate for fTable=cTable .and. (fRecno=nRecno .or. fRecno=0) .and. fMachine!=gcMachinename nLoop=nLoop-1 enddo if nLoop<=0 wait wind "Lock failed" nowait use =fxFlushLock(cLockHndl) sele (nOldArea) return .F. endif wait wind "pl" nowait && PASS LOCK locate for fTable=cTable .and. fMachine=gcMachineName if ! found() locate for fTable=" " if ! found() appe blank endif repl fTable with cTable, fMachine with gcMachineName endif repl fRecno with nRecno, fSeconds with int(seconds()) *CLEAR UNFINISH WORK =fxClearLongLock() use =fxFlushLock(cLockHndl) sele (nOldArea) return .T. function fxClearLongLock nSeconds=seconds() repl fTable with " ", fMachine with " "; fRecno with 0, fSeconds with 0; for fMachine=gcMachineName .and. abs(fSeconds-nSeconds)>120; .and. fSeconds!=0 return .T. function fxCrLockHndl cLockHndl='lockhndl' if ! file(cLockHndl+'.dbf') create table (cLockHndl) (; fTable c (20); ,fRecno n (10); ,fMachine c (20); ,fSeconds n (10) ) endif return .T. function fxUnlock * SOLVED LOCKED FAIL IN LINUX * BY CREATE NEW LOCKED TABLE TO HANDLE LOCKED STATE * UNLOCK [IN <expN> | <expC> | '*ALL'] parameter xArea cLockHndl='lockhndl' =fxCrLockHndl() nOldArea=sele() xTemp=type('xArea') if xTemp='C' .and. upper(xArea)='*ALL' sele 0 =fxFlushLock(cLockHndl) use lockhndl repl fTable with ' ', fRecno with 0; , fMachine with ' ', fSeconds with 0; for fMachine=gcMachineName *CLEAR UNFINISH WORK =fxClearLongLock() use =fxFlushLock(cLockHndl) sele (nOldArea) return .T. endif if xTemp='N' .or. xTemp='C' sele (xArea) else if len(dbf())=0 return .F. endif endif cTable=fjuststem(dbf()) if file(cTable+'.LCK') dele file (cTable+'.LCK') endif sele 0 =fxFlushLock(cLockHndl) use (cLockHndl) repl fTable with ' ', fRecno with 0; , fMachine with ' ', fSeconds with 0; for fMachine=gcMachineName .and. fTable=cTable *CLEAR UNFINISH WORK =fxClearLongLock() use =fxFlushLock(cLockHndl) sele (nOldArea) return .T.
เรียกใช้งานด้วย
=fxflock() && IN CURRENT AREA
...DO UPDATE...
=fxunlock()
และ
=fxrlock() && IN CURRENT AREA AND CURRENT RECORD
...DO UPDATE...
=fxunlock()
ลองใช้งานแล้วได้ผลดีพอควร แต่ยังขาดความสมบูรณ์อีกมาก ถ้าจะนำไปใช้จริงควรปรับแก้เอาเองครับ