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/smbdata -o username=USER,password=PASSWORD,iocharset=utf8,directio
จะสามารถล๊อกเรคคอร์ดได้ตรงและแก้ปัญหาแคชไฟล์ได้แล้วครับ
*****
ข้อความเดิม
เที่ยวนี้ ถือเป็นการบันทึกกันลืม เพราะยังไม่มีความแน่ใจในการทดสอบ
สำหรับเครื่องลูกข่ายที่เป็นวินโดวส์ ใช้งานได้ดีไม่มีปัญหา
แต่สำหรับเครื่องลูกข่ายที่เป็นลินุกซ์ ได้เคยทดสอบมานานแล้วเกี่ยวกับการเมานต์ samba แบบต่าง ๆ
ช่วงนี้ได้มีโอกาสมาทดสอบดูอีกครั้งนึง พบว่า...
ดูแนวโน้มแล้ว อนาคตคงจะไปลงที่ cifs ทั้งหมด แต่สำหรับตอนนี้ ถ้าต้องมีการล๊อกเรคคอร์ดข้ามแพลตฟอร์มระหว่างวินโดวส์กับลินุกส์ คงต้องเลือกใช้ smbfs โดยเขียนรูทีนการล๊อกไฟล์และเรคคอร์ดขึ้นมาใช้เอง น่าจะเป็นทางออกที่ดีที่สุด
บันทึกลิงก์ชั้นดีของ samba อีกนิด
ติดตั้ง Visual Foxpro เสร็จแล้ว เกิดปัญหาว่า MSDN คือ ความช่วยเหลือใช้งานไม่ได้
แก้ปํญหาด้วยการใช้ Network Drive Letter แทน Network Reference
เช่นสมมุติว่าเราแม็ปไดร์ฟ G: จาก \\server\app
เวลาเขาขึ้นมาถามหาไฟล์ ให้เราเปลี่ยนจาก \\server\app เป็น g:\ แทน
ก็จะสามารถใช้งานได้
ปกติใช้งาน samba โดยใช้เครื่องลูกข่ายที่เป็นแบบไอพีคงที่ตลอด
เนื่องจากมักมีเครื่องพิมพ์หรืออุปกรณ์อื่นที่ติดกับเครื่องลูกข่ายนั้น ๆ
เมื่อเราต้องการใช้งานเครื่องพิมพ์ การอ้างถึงไอพีตรง ๆ หรืออ้างชื่อโดยผ่าน dns ภายใน จะทำได้สะดวกกว่า
วันนี้ซ่อมเครื่องลูกข่ายไปเครื่องนึง ลืมกำหนดไอพีแอดเดรส ไอพีของเครื่องที่ซ่อมจึงถูกกำหนดโดย dhcp server
ผลคือทำให้ samba ป่วนไปหมด ในทุกเครื่องลูกข่ายที่เป็นวินโดวส์
ค้นไปค้นมาจึงพบว่าลืมตั้งไอพีสำหรับเครื่องที่ทำการซ๋อม พอกำหนดไอพีเสร็จ ปัญหาก็เรียบร้อย
เข้าใจว่า samba คงเก็บค่าไอพีในการล๊อกไฟล์และเรคคอร์ดเป็นหลัก ดังนั้นเมื่อไอพีเปลียนไป ทำให้ samba รับภาระโหลดมากเกินไป จึงทำให้ป่วนดังกล่าว
จึงบันทึกไว้เตือนความจำว่า สำหรับ samba แล้ว ถ้ามีการใช้งานผสมกันระหว่าง ลินุกส์และวินโดวส์ ควรกำหนดไอพีให้เครื่องลูกข่ายเป็นแบบคงที่เท่านั้น
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()
ลองใช้งานแล้วได้ผลดีพอควร แต่ยังขาดความสมบูรณ์อีกมาก ถ้าจะนำไปใช้จริงควรปรับแก้เอาเองครับ