Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
W
wendelin.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Kirill Smelkov
wendelin.core
Commits
9a66005f
Commit
9a66005f
authored
Nov 23, 2018
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
c71be2d4
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
513 additions
and
199 deletions
+513
-199
wcfs/misc.go
wcfs/misc.go
+142
-0
wcfs/todo.dot
wcfs/todo.dot
+7
-0
wcfs/todo.svg
wcfs/todo.svg
+153
-114
wcfs/wcfs.go
wcfs/wcfs.go
+210
-84
wcfs/wcfs_test.py
wcfs/wcfs_test.py
+1
-1
No files found.
wcfs/misc.go
View file @
9a66005f
...
...
@@ -23,6 +23,7 @@ package main
import
(
"context"
"fmt"
"sync/atomic"
"syscall"
log
"github.com/golang/glog"
...
...
@@ -31,6 +32,9 @@ import (
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/pkg/errors"
"lab.nexedi.com/kirr/go123/xcontext"
"lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/neo/go/zodb"
)
...
...
@@ -163,6 +167,7 @@ func mount(mntpt string, root nodefs.Node, opts *fuse.MountOptions) (*fuse.Serve
return
fssrv
,
fsconn
,
nil
}
// ---- ZODB ---
// typeOf returns ZODB type of an object.
//
...
...
@@ -171,3 +176,140 @@ func mount(mntpt string, root nodefs.Node, opts *fuse.MountOptions) (*fuse.Serve
func
typeOf
(
obj
interface
{})
string
{
return
zodb
.
ClassOf
(
obj
)
}
// ZConn is zodb.Connection + associated read-only transaction under which
// objects of the connection are accessed.
type
ZConn
struct
{
*
zodb
.
Connection
// read-only transaction under which we access zodb.Connection data.
txnCtx
context
.
Context
// XXX -> better directly store txn
// for historic @<rev> acess the connection can be shared between several bigfiles.
// since we want to free such connections when no longer needed we
// return zodb.Connection back to zodb.DB when refcnt drops to 0.
refcnt
int32
}
// zopen opens new connection to ZODB database + associated read-only transaction.
func
zopen
(
ctx
context
.
Context
,
zdb
*
zodb
.
DB
,
zopt
*
zodb
.
ConnOptions
)
(
_
*
ZConn
,
err
error
)
{
// create new read-only transaction
txn
,
txnCtx
:=
transaction
.
New
(
context
.
Background
())
defer
func
()
{
if
err
!=
nil
{
txn
.
Abort
()
}
}()
// XXX better ctx = transaction.PutIntoContext(ctx, txn)
ctx
,
cancel
:=
xcontext
.
Merge
(
ctx
,
txnCtx
)
defer
cancel
()
zconn
,
err
:=
zdb
.
Open
(
ctx
,
zopt
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
ZConn
{
Connection
:
zconn
,
txnCtx
:
txnCtx
,
refcnt
:
1
,
},
nil
}
// Release decrements reference count and releases connection back to zodb.DB
// if it is no longer used.
func
(
zc
*
ZConn
)
Release
()
{
rc
:=
atomic
.
AddInt32
(
&
zc
.
refcnt
,
-
1
)
if
rc
<
0
{
panic
(
"ZConn.Release: refcnt < 0"
)
}
if
rc
>
0
{
return
}
txn
:=
transaction
.
Current
(
zc
.
txnCtx
)
txn
.
Abort
()
zc
.
Connection
=
nil
zc
.
txnCtx
=
nil
}
// Incref increments connection's reference counter by 1.
func
(
zc
*
ZConn
)
Incref
()
{
rc
:=
atomic
.
AddInt32
(
&
zc
.
refcnt
,
+
1
)
if
rc
<=
1
{
panic
(
"Zconn.Incref: refcnt was < 1"
)
}
}
// zopenAt opens historic connection @<rev>.
//
// if the connection for this @<rev> was already opened - it is shared.
func
(
r
*
Root
)
zopenAt
(
ctx
context
.
Context
,
rev
zodb
.
Tid
)
(
_
*
ZConn
,
err
error
)
{
// check if zconn for @<rev> is already there
r
.
zrevMu
.
Lock
()
zconn
:=
r
.
zrevTab
[
rev
]
if
zconn
!=
nil
{
if
atomic
.
LoadInt32
(
&
zconn
.
refcnt
)
>
0
{
zconn
.
Incref
()
}
else
{
zconn
=
nil
// in-progress destruction
}
}
r
.
zrevMu
.
Unlock
()
if
zconn
!=
nil
{
return
zconn
,
nil
}
// not there - without zrevMu lock proceed to open it
zconn
,
err
=
zopen
(
ctx
,
r
.
zdb
,
&
zodb
.
ConnOptions
{
At
:
rev
})
if
err
!=
nil
{
return
nil
,
err
}
// relock zrevTab and either register zconn, or returun another zconn2,
// that might have been opened while we were not holding zrevMu.
r
.
zrevMu
.
Lock
()
defer
r
.
zrevMu
.
Unlock
()
zconn2
:=
r
.
zrevTab
[
rev
]
if
zconn2
!=
nil
{
if
atomic
.
LoadInt32
(
&
zconn2
.
refcnt
)
>
0
{
zconn
.
Release
()
zconn2
.
Incref
()
return
zconn2
,
nil
}
// else it was another in-progress destruction
}
r
.
zrevTab
[
rev
]
=
zconn
// schedule del zrevTab[rev] on zconn destroy
txn
:=
transaction
.
Current
(
zconn
.
txnCtx
)
txn
.
RegisterSync
(
&
zrevTabUnregister
{
r
,
zconn
})
return
zconn
,
nil
}
// zrevTabUnregister unregisters zconn from root.zrevTab on zconn's transaction abort.
type
zrevTabUnregister
struct
{
root
*
Root
zconn
*
ZConn
}
func
(
u
*
zrevTabUnregister
)
BeforeCompletion
(
txn
transaction
.
Transaction
)
{}
func
(
u
*
zrevTabUnregister
)
AfterCompletion
(
txn
transaction
.
Transaction
)
{
rev
:=
u
.
zconn
.
At
()
u
.
root
.
zrevMu
.
Lock
()
defer
u
.
root
.
zrevMu
.
Unlock
()
// delete only if zconn is still registered - as another zconn2 might have
// been already registered instead while zconn was in zrevTab with refcnt=0.
if
u
.
root
.
zrevTab
[
rev
]
==
u
.
zconn
{
delete
(
u
.
root
.
zrevTab
,
rev
)
}
}
wcfs/todo.dot
View file @
9a66005f
...
...
@@ -25,6 +25,10 @@ digraph {
wcfsInvProcess
->
δ
Ftail
;
wcfsInvProcess
->
fuseRetrieveCache
;
ZODB_go_inv
->
fs1_go_inv
;
ZODB_go_inv
->
zeo_go_inv
;
ZODB_go_inv
->
neo_go_inv
;
wcfsRead
->
blktabGet
;
wcfsRead
->
δ
Ftail
;
wcfsRead
->
mappingRegister
;
...
...
@@ -79,6 +83,9 @@ digraph {
headInv
[
label
=
"#blk ← head/inv."
]
ZODB_go_inv
[
label
=
"ZODB/go\ninvalidations"
]
fs1_go_inv
[
label
=
"fs1/go\ninvalidations"
]
zeo_go_inv
[
label
=
"zeo/go\ninvalidations"
]
neo_go_inv
[
label
=
"neo/go\ninvalidations"
]
// Btree_read [label="BTree read", style=filled fillcolor=lightyellow]
// ZBlk_read [label="ZBigFile / ZBlk* read", style=filled fillcolor=lightyellow]
// ZODB_read [label="ZODB deserialize object", style=filled fillcolor=lightyellow]
...
...
wcfs/todo.svg
View file @
9a66005f
...
...
@@ -4,264 +4,303 @@
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
-->
<!-- Title: %3 Pages: 1 -->
<svg
width=
"1
290pt"
height=
"385
pt"
viewBox=
"0.00 0.00 1
290.46 385.22
"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<g
id=
"graph0"
class=
"graph"
transform=
"scale(1 1) rotate(0) translate(4 3
81.2203
)"
>
<svg
width=
"1
449pt"
height=
"403
pt"
viewBox=
"0.00 0.00 1
449.46 402.96
"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<g
id=
"graph0"
class=
"graph"
transform=
"scale(1 1) rotate(0) translate(4 3
98.9605
)"
>
<title>
%3
</title>
<polygon
fill=
"#ffffff"
stroke=
"transparent"
points=
"-4,4 -4,-3
81.2203 1286.4565,-381.2203 1286
.4565,4 -4,4"
/>
<polygon
fill=
"#ffffff"
stroke=
"transparent"
points=
"-4,4 -4,-3
98.9605 1445.4565,-398.9605 1445
.4565,4 -4,4"
/>
<!-- wcfs -->
<g
id=
"node1"
class=
"node"
>
<title>
wcfs
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
595.8112"
cy=
"-350.3503
"
rx=
"27.0966"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
595.8112"
y=
"-346.6503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
wcfs
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
754.8112"
cy=
"-368.0904
"
rx=
"27.0966"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
754.8112"
y=
"-364.3904
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
wcfs
</text>
</g>
<!-- invProto -->
<g
id=
"node2"
class=
"node"
>
<title>
invProto
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
801.8112"
cy=
"-350.3503
"
rx=
"78.4777"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
801.8112"
y=
"-354.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
open/invalidation
</text>
<text
text-anchor=
"middle"
x=
"
801.8112"
y=
"-339.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
protocol
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
960.8112"
cy=
"-368.0904
"
rx=
"78.4777"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
960.8112"
y=
"-371.8904
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
open/invalidation
</text>
<text
text-anchor=
"middle"
x=
"
960.8112"
y=
"-356.8904
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
protocol
</text>
</g>
<!-- wcfs->invProto -->
<g
id=
"edge1"
class=
"edge"
>
<title>
wcfs
->
invProto
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
623.1706,-350.3503C653.1593,-350.3503 683.1481,-350.3503 713.1369,-350.3503
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
713.307,-353.8504 723.307,-350.3503 713.307,-346.8504 713.307,-353.8504
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
782.1706,-368.0904C812.1593,-368.0904 842.1481,-368.0904 872.1369,-368.0904
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
872.307,-371.5905 882.307,-368.0904 872.307,-364.5905 872.307,-371.5905
"
/>
</g>
<!-- autoexit -->
<g
id=
"node4"
class=
"node"
>
<title>
autoexit
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
363.8112"
cy=
"-260.6102
"
rx=
"52.1524"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
363.8112"
y=
"-264.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
autoexit
</text>
<text
text-anchor=
"middle"
x=
"
363.8112"
y=
"-249.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
if !activity
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
522.8112"
cy=
"-278.3503
"
rx=
"52.1524"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
522.8112"
y=
"-282.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
autoexit
</text>
<text
text-anchor=
"middle"
x=
"
522.8112"
y=
"-267.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
if !activity
</text>
</g>
<!-- wcfs->autoexit -->
<g
id=
"edge3"
class=
"edge"
>
<title>
wcfs
->
autoexit
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
571.5583,-341.972C538.2492,-330.3431 476.5811,-308.3754 424.8112,-287.4802 420.8876,-285.8966 416.8405,-284.2173 412.7852,-282.5018
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
413.9322,-279.1855 403.3621,-278.4616 411.1737,-285.6191 413.9322,-279.1855
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
730.5583,-359.7121C697.2492,-348.0832 635.5811,-326.1155 583.8112,-305.2203 579.8876,-303.6367 575.8405,-301.9574 571.7852,-300.2419
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
572.9322,-296.9256 562.3621,-296.2017 570.1737,-303.3592 572.9322,-296.9256
"
/>
</g>
<!-- wcfsInvProcess -->
<g
id=
"node5"
class=
"node"
>
<title>
wcfsInvProcess
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
523.8112"
cy=
"-260.6102
"
rx=
"89.6056"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
523.8112"
y=
"-264.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
process
</text>
<text
text-anchor=
"middle"
x=
"
523.8112"
y=
"-249.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
ZODB invalidations
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
682.8112"
cy=
"-278.3503
"
rx=
"89.6056"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
682.8112"
y=
"-282.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
process
</text>
<text
text-anchor=
"middle"
x=
"
682.8112"
y=
"-267.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
ZODB invalidations
</text>
</g>
<!-- wcfs->wcfsInvProcess -->
<g
id=
"edge4"
class=
"edge"
>
<title>
wcfs
->
wcfsInvProcess
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
582.939,-334.3066C574.2299,-323.4516 562.3891,-308.6933 551.5908,-295.2344
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
554.0273,-292.6783 545.0393,-287.0687 548.5674,-297.0589 554.0273,-292.6783
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
741.939,-352.0467C733.2299,-341.1917 721.3891,-326.4335 710.5908,-312.9745
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
713.0273,-310.4184 704.0393,-304.8088 707.5674,-314.799 713.0273,-310.4184
"
/>
</g>
<!-- wcfsRead -->
<g
id=
"node6"
class=
"node"
>
<title>
wcfsRead
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
833.8112"
cy=
"-260.6102
"
rx=
"47.3916"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
833.8112"
y=
"-256.9102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
read(#blk)
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
992.8112"
cy=
"-278.3503
"
rx=
"47.3916"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
992.8112"
y=
"-274.6503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
read(#blk)
</text>
</g>
<!-- wcfs->wcfsRead -->
<g
id=
"edge5"
class=
"edge"
>
<title>
wcfs
->
wcfsRead
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
619.9416,-341.2517C659.9371,-326.171 740.5048,-295.7922 790.532,-276.929
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
791.9871,-280.121 800.1092,-273.3178 789.5173,-273.5711 791.9871,-280.12
1"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
778.9416,-358.9918C818.9371,-343.9111 899.5048,-313.5323 949.532,-294.6691
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
950.9871,-297.8611 959.1092,-291.058 948.5173,-291.3112 950.9871,-297.861
1"
/>
</g>
<!-- client -->
<g
id=
"node3"
class=
"node"
>
<title>
client
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
969.8112"
cy=
"-350.3503
"
rx=
"30.5947"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
969.8112"
y=
"-346.6503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
client
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
1128.8112"
cy=
"-368.0904
"
rx=
"30.5947"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
1128.8112"
y=
"-364.3904
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
client
</text>
</g>
<!-- invProto->client -->
<g
id=
"edge2"
class=
"edge"
>
<title>
invProto
->
client
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
890.628,-350.3503C906.7958,-350.3503 922.9636,-350.3503 939.1315,-350.3503
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
890.5612,-346.8504 880.5612,-350.3503 890.5611,-353.8504 890.5612,-346.8504
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
1049.628,-368.0904C1065.7958,-368.0904 1081.9636,-368.0904 1098.1315,-368.0904
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
1049.5612,-364.5905 1039.5612,-368.0904 1049.5611,-371.5905 1049.5612,-364.5905
"
/>
</g>
<!-- client->wcfsRead -->
<g
id=
"edge
18
"
class=
"edge"
>
<g
id=
"edge
21
"
class=
"edge"
>
<title>
client
->
wcfsRead
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
949.4506,-336.9153C927.3478,-322.3307 891.7503,-298.8415 865.8833,-281.7731
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
867.7919,-278.8392 857.5176,-276.2529 863.9366,-284.6819 867.7919,-278.8392
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
1108.4506,-354.6554C1086.3478,-340.0708 1050.7503,-316.5816 1024.8833,-299.5132
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
1026.7919,-296.5793 1016.5176,-293.993 1022.9366,-302.422 1026.7919,-296.5793
"
/>
</g>
<!-- mappingRegister -->
<g
id=
"node1
3
"
class=
"node"
>
<g
id=
"node1
6
"
class=
"node"
>
<title>
mappingRegister
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
000.8112"
cy=
"-170.8701
"
rx=
"65.1077"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"1
000.8112"
y=
"-174.6701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
mmappings:
</text>
<text
text-anchor=
"middle"
x=
"1
000.8112"
y=
"-159.6701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
register/maint
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
159.8112"
cy=
"-188.6102
"
rx=
"65.1077"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"1
159.8112"
y=
"-192.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
mmappings:
</text>
<text
text-anchor=
"middle"
x=
"1
159.8112"
y=
"-177.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
register/maint
</text>
</g>
<!-- client->mappingRegister -->
<g
id=
"edge
19
"
class=
"edge"
>
<g
id=
"edge
22
"
class=
"edge"
>
<title>
client
->
mappingRegister
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
972.621,-332.1459C976.2019,-309.1786 982.6665,-268.4812 988.8112,-233.7401 990.2972,-225.3386 991.9712,-216.3212 993.5869,-207.8132
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
997.0681,-208.2434 995.5122,-197.7635 990.1931,-206.9263 997.0681,-208.2434
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
1131.621,-349.886C1135.2019,-326.9188 1141.6665,-286.2213 1147.8112,-251.4802 1149.2972,-243.0787 1150.9712,-234.0613 1152.5869,-225.5533
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
1156.0681,-225.9835 1154.5122,-215.5036 1149.1931,-224.6664 1156.0681,-225.9835
"
/>
</g>
<!-- clientInvHandle -->
<g
id=
"node
18
"
class=
"node"
>
<g
id=
"node
21
"
class=
"node"
>
<title>
clientInvHandle
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
078.8112"
cy=
"-260.6102
"
rx=
"80.7205"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"1
078.8112"
y=
"-264.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
process
</text>
<text
text-anchor=
"middle"
x=
"1
078.8112"
y=
"-249.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
#blk invalidations
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
237.8112"
cy=
"-278.3503
"
rx=
"80.7205"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"1
237.8112"
y=
"-282.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
process
</text>
<text
text-anchor=
"middle"
x=
"1
237.8112"
y=
"-267.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
#blk invalidations
</text>
</g>
<!-- client->clientInvHandle -->
<g
id=
"edge2
0
"
class=
"edge"
>
<g
id=
"edge2
3
"
class=
"edge"
>
<title>
client
->
clientInvHandle
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
987.8103,-335.5315C1002.0729,-323.7891 1022.5081,-306.9648 1040.4013,-292.2332
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1
042.9168,-294.6957 1048.4124,-285.6376 1038.4676,-289.2916 1042.9168,-294.6957
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
1146.8103,-353.2716C1161.0729,-341.5292 1181.5081,-324.7049 1199.4013,-309.9733
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1
201.9168,-312.4359 1207.4124,-303.3777 1197.4676,-307.0317 1201.9168,-312.4359
"
/>
</g>
<!-- nowcfs -->
<g
id=
"node
19
"
class=
"node"
>
<g
id=
"node
22
"
class=
"node"
>
<title>
nowcfs
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
229.8112"
cy=
"-260.6102
"
rx=
"52.7911"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"1
229.8112"
y=
"-256.9102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
!wcfs mode
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
388.8112"
cy=
"-278.3503
"
rx=
"52.7911"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"1
388.8112"
y=
"-274.6503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
!wcfs mode
</text>
</g>
<!-- client->nowcfs -->
<g
id=
"edge2
1
"
class=
"edge"
>
<g
id=
"edge2
4
"
class=
"edge"
>
<title>
client
->
nowcfs
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
997.4952,-342.6396C1036.2362,-331.616 1108.6328,-310.1634 1168.8112,-287.4802 1175.359,-285.0121 1182.217,-282.2108 1188.8732,-279.3714
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1
190.6473,-282.4166 1198.4215,-275.2185 1187.8553,-275.9975 1190.6473,-282.4166
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
1156.4952,-360.3797C1195.2362,-349.3561 1267.6328,-327.9035 1327.8112,-305.2203 1334.359,-302.7523 1341.217,-299.9509 1347.8732,-297.1115
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1
349.6473,-300.1567 1357.4215,-292.9586 1346.8553,-293.7376 1349.6473,-300.1567
"
/>
</g>
<!-- ZODB_go_inv -->
<g
id=
"node7"
class=
"node"
>
<title>
ZODB_go_inv
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
60.8112"
cy=
"-170.8701
"
rx=
"60.623"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
60.8112"
y=
"-174.6701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
ZODB/go
</text>
<text
text-anchor=
"middle"
x=
"
60.8112"
y=
"-159.6701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
invalidations
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
210.8112"
cy=
"-188.6102
"
rx=
"60.623"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
210.8112"
y=
"-192.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
ZODB/go
</text>
<text
text-anchor=
"middle"
x=
"
210.8112"
y=
"-177.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
invalidations
</text>
</g>
<!-- wcfsInvProcess->ZODB_go_inv -->
<g
id=
"edge6"
class=
"edge"
>
<title>
wcfsInvProcess
->
ZODB_go_inv
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
459.6883,-241.7734C448.145,-238.7929 436.1713,-235.9654 424.8112,-233.7401 295.6238,-208.4342 258.5488,-229.5659 130.8112,-197.7401 125.4429,-196.4026 119.9278,-194.7686 114.4625,-192.9735
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
115.3162,-189.5652 104.7214,-189.5974 113.0239,-196.1792 115.3162,-189.5652
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
618.6883,-259.5135C607.145,-256.5331 595.1713,-253.7055 583.8112,-251.4802 454.6238,-226.1743 418.1401,-244.831 289.8112,-215.4802 282.2646,-213.7542 274.4264,-211.578 266.747,-209.2147
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
267.7297,-205.8544 257.1388,-206.141 265.5968,-212.5216 267.7297,-205.8544
"
/>
</g>
<!-- zconnCacheGet -->
<g
id=
"node8"
class=
"node"
>
<title>
zconnCacheGet
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
210.8112"
cy=
"-170.8701
"
rx=
"71.4873"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
210.8112"
y=
"-167.1701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
zconn.Cache.Get
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
369.8112"
cy=
"-188.6102
"
rx=
"71.4873"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
369.8112"
y=
"-184.9102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
zconn.Cache.Get
</text>
</g>
<!-- wcfsInvProcess->zconnCacheGet -->
<g
id=
"edge7"
class=
"edge"
>
<title>
wcfsInvProcess
->
zconnCacheGet
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
457.2468,-242.5369C446.406,-239.5954 435.2965,-236.5821 424.8112,-233.7401 365.7056,-217.7198 350.4515,-215.3877 291.8112,-197.7401 282.341,-194.8901 272.3025,-191.6995 262.6358,-188.5398
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
263.7211,-185.2123 253.128,-185.404 261.5285,-191.8601 263.7211,-185.2123
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
616.2468,-260.277C605.406,-257.3355 594.2965,-254.3222 583.8112,-251.4802 524.7056,-235.4599 509.4515,-233.1278 450.8112,-215.4802 441.341,-212.6302 431.3025,-209.4397 421.6358,-206.2799
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
422.7211,-202.9524 412.128,-203.1441 420.5285,-209.6002 422.7211,-202.9524
"
/>
</g>
<!-- zobj2file -->
<g
id=
"node9"
class=
"node"
>
<title>
zobj2file
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
370.8112"
cy=
"-170.8701
"
rx=
"70.3881"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
370.8112"
y=
"-167.1701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
Z* → file/[]#blk
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
529.8112"
cy=
"-188.6102
"
rx=
"70.3881"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
529.8112"
y=
"-184.9102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
Z* → file/[]#blk
</text>
</g>
<!-- wcfsInvProcess->zobj2file -->
<g
id=
"edge8"
class=
"edge"
>
<title>
wcfsInvProcess
->
zobj2file
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
482.81,-236.5615C459.5232,-222.9029 430.5977,-205.937 407.9761,-192.6686
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
409.7252,-189.6369 399.3286,-187.5966 406.1836,-195.675 409.7252,-189.6369
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
641.81,-254.3016C618.5232,-240.643 589.5977,-223.6771 566.9761,-210.4087
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
568.7252,-207.3771 558.3286,-205.3367 565.1836,-213.4151 568.7252,-207.3771
"
/>
</g>
<!-- δFtail -->
<g
id=
"node10"
class=
"node"
>
<title>
δFtail
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
638.8112"
cy=
"-170.8701
"
rx=
"31.6951"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
638.8112"
y=
"-167.1701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
δFtail
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
797.8112"
cy=
"-188.6102
"
rx=
"31.6951"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
797.8112"
y=
"-184.9102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
δFtail
</text>
</g>
<!-- wcfsInvProcess->δFtail -->
<g
id=
"edge9"
class=
"edge"
>
<title>
wcfsInvProcess
->
δFtail
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
556.1511,-235.3737C573.6886,-221.6884 595.1158,-204.9677 611.7595,-191.9798
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
614.2507,-194.4754 619.9812,-185.564 609.9443,-188.9568 614.2507,-194.4754
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
715.1511,-253.1138C732.6886,-239.4285 754.1158,-222.7078 770.7595,-209.7199
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
773.2507,-212.2155 778.9812,-203.3041 768.9443,-206.6969 773.2507,-212.2155
"
/>
</g>
<!-- fuseRetrieveCache -->
<g
id=
"node11"
class=
"node"
>
<title>
fuseRetrieveCache
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
523.8112"
cy=
"-170.8701
"
rx=
"65.1077"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
523.8112"
y=
"-174.6701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
FUSE:
</text>
<text
text-anchor=
"middle"
x=
"
523.8112"
y=
"-159.6701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
retrieve cache
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
682.8112"
cy=
"-188.6102
"
rx=
"65.1077"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
682.8112"
y=
"-192.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
FUSE:
</text>
<text
text-anchor=
"middle"
x=
"
682.8112"
y=
"-177.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
retrieve cache
</text>
</g>
<!-- wcfsInvProcess->fuseRetrieveCache -->
<g
id=
"edge10"
class=
"edge"
>
<title>
wcfsInvProcess
->
fuseRetrieveCache
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
523.8112,-233.6852C523.8112,-225.6102 523.8112,-216.607 523.8112,-207.9961
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
527.3113,-207.776 523.8112,-197.776 520.3113,-207.7761 527.3113,-207.776
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
682.8112,-251.4253C682.8112,-243.3504 682.8112,-234.3471 682.8112,-225.7362
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
686.3113,-225.5161 682.8112,-215.5162 679.3113,-225.5162 686.3113,-225.5161
"
/>
</g>
<!-- wcfsRead->δFtail -->
<g
id=
"edge1
2
"
class=
"edge"
>
<g
id=
"edge1
5
"
class=
"edge"
>
<title>
wcfsRead
->
δFtail
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
798.4077,-248.6152C766.3717,-237.2133 718.3024,-218.7244 678.8112,-197.7401 674.9881,-195.7086 671.0761,-193.4005 667.2674,-191.0105
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
668.9424,-187.9238 658.6582,-185.3771 665.1096,-193.7813 668.9424,-187.9238
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
957.4077,-266.3553C925.3717,-254.9534 877.3024,-236.4646 837.8112,-215.4802 833.9881,-213.4488 830.0761,-211.1406 826.2674,-208.7506
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
827.9424,-205.664 817.6582,-203.1172 824.1096,-211.5214 827.9424,-205.664
"
/>
</g>
<!-- blktabGet -->
<g
id=
"node1
2
"
class=
"node"
>
<g
id=
"node1
5
"
class=
"node"
>
<title>
blktabGet
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
802.8112"
cy=
"-170.8701
"
rx=
"114.6026"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
802.8112"
y=
"-174.6701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
blktab.Get(#blk):
</text>
<text
text-anchor=
"middle"
x=
"
802.8112"
y=
"-159.6701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
manually + → ⌈rev(#blk)⌉
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
961.8112"
cy=
"-188.6102
"
rx=
"114.6026"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"
961.8112"
y=
"-192.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
blktab.Get(#blk):
</text>
<text
text-anchor=
"middle"
x=
"
961.8112"
y=
"-177.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
manually + → ⌈rev(#blk)⌉
</text>
</g>
<!-- wcfsRead->blktabGet -->
<g
id=
"edge1
1
"
class=
"edge"
>
<g
id=
"edge1
4
"
class=
"edge"
>
<title>
wcfsRead
->
blktabGet
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
827.5379,-242.4499C824.0824,-232.4469 819.665,-219.6593 815.5225,-207.6673
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
818.7079,-206.1688 812.1345,-197.8597 812.0915,-208.4544 818.7079,-206.1688
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
986.5379,-260.19C983.0824,-250.187 978.665,-237.3994 974.5225,-225.4074
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
977.7079,-223.9089 971.1345,-215.5998 971.0915,-226.1946 977.7079,-223.9089
"
/>
</g>
<!-- wcfsRead->mappingRegister -->
<g
id=
"edge1
3
"
class=
"edge"
>
<g
id=
"edge1
6
"
class=
"edge"
>
<title>
wcfsRead
->
mappingRegister
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
861.3878,-245.7914C885.9872,-232.5726 922.5709,-212.9137 952.0922,-197.05
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
953.8541,-200.0766 961.0061,-192.2599 950.5406,-193.9104 953.8541,-200.0766
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
1020.3878,-263.5315C1044.9872,-250.3127 1081.5709,-230.6538 1111.0922,-214.7901
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
1112.8541,-217.8167 1120.0061,-210.0001 1109.5406,-211.6506 1112.8541,-217.8167
"
/>
</g>
<!-- headInv -->
<g
id=
"node1
4
"
class=
"node"
>
<g
id=
"node1
7
"
class=
"node"
>
<title>
headInv
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
156.8112"
cy=
"-170.8701
"
rx=
"73.387"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"1
156.8112"
y=
"-167.1701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
#blk ← head/inv.
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
315.8112"
cy=
"-188.6102
"
rx=
"73.387"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"1
315.8112"
y=
"-184.9102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
#blk ← head/inv.
</text>
</g>
<!-- wcfsRead->headInv -->
<g
id=
"edge1
4
"
class=
"edge"
>
<g
id=
"edge1
7
"
class=
"edge"
>
<title>
wcfsRead
->
headInv
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M874.103,-250.9033C922.1291,-239.148 1004.7014,-218.3507 1074.8112,-197.7401 1084.2972,-194.9515 1094.3423,-191.8147 1104.0277,-188.6947"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1105.1326,-192.0159 1113.5592,-185.5939 1102.9669,-185.3593 1105.1326,-192.0159"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M1033.103,-268.6434C1081.1291,-256.8881 1163.7014,-236.0908 1233.8112,-215.4802 1243.2972,-212.6916 1253.3423,-209.5548 1263.0277,-206.4349"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1264.1326,-209.756 1272.5592,-203.334 1261.9669,-203.0995 1264.1326,-209.756"
/>
</g>
<!-- fs1_go_inv -->
<g
id=
"node12"
class=
"node"
>
<title>
fs1_go_inv
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"60.8112"
cy=
"-98.8701"
rx=
"60.623"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"60.8112"
y=
"-102.6701"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
fs1/go
</text>
<text
text-anchor=
"middle"
x=
"60.8112"
y=
"-87.6701"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
invalidations
</text>
</g>
<!-- ZODB_go_inv->fs1_go_inv -->
<g
id=
"edge11"
class=
"edge"
>
<title>
ZODB_go_inv
->
fs1_go_inv
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M174.5004,-166.8866C153.8487,-154.5314 127.8076,-138.9518 105.8607,-125.8217"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"107.4257,-122.6795 97.0473,-120.5489 103.8318,-128.6865 107.4257,-122.6795"
/>
</g>
<!-- zeo_go_inv -->
<g
id=
"node13"
class=
"node"
>
<title>
zeo_go_inv
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"200.8112"
cy=
"-98.8701"
rx=
"60.623"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"200.8112"
y=
"-102.6701"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
zeo/go
</text>
<text
text-anchor=
"middle"
x=
"200.8112"
y=
"-87.6701"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
invalidations
</text>
</g>
<!-- ZODB_go_inv->zeo_go_inv -->
<g
id=
"edge12"
class=
"edge"
>
<title>
ZODB_go_inv
->
zeo_go_inv
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M207.8109,-161.6852C206.911,-153.6102 205.9078,-144.607 204.9482,-135.9961"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"208.3954,-135.3269 203.8094,-125.776 201.4385,-136.1022 208.3954,-135.3269"
/>
</g>
<!-- neo_go_inv -->
<g
id=
"node14"
class=
"node"
>
<title>
neo_go_inv
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"340.8112"
cy=
"-98.8701"
rx=
"60.623"
ry=
"26.7407"
/>
<text
text-anchor=
"middle"
x=
"340.8112"
y=
"-102.6701"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
neo/go
</text>
<text
text-anchor=
"middle"
x=
"340.8112"
y=
"-87.6701"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
invalidations
</text>
</g>
<!-- ZODB_go_inv->neo_go_inv -->
<g
id=
"edge13"
class=
"edge"
>
<title>
ZODB_go_inv
->
neo_go_inv
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M243.6157,-165.965C260.5514,-154.2741 281.3882,-139.8902 299.42,-127.4428"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"301.7759,-130.0694 308.0172,-121.508 297.7992,-124.3087 301.7759,-130.0694"
/>
</g>
<!-- zblk2file -->
<g
id=
"node1
5
"
class=
"node"
>
<g
id=
"node1
8
"
class=
"node"
>
<title>
zblk2file
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
264.8112"
cy=
"-90
"
rx=
"83.3857"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
264.8112"
y=
"-86.3
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
ZBlk* → file/[]#blk
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
740.8112"
cy=
"-98.8701
"
rx=
"83.3857"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
740.8112"
y=
"-95.1701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
ZBlk* → file/[]#blk
</text>
</g>
<!-- zobj2file->zblk2file -->
<g
id=
"edge1
5
"
class=
"edge"
>
<g
id=
"edge1
8
"
class=
"edge"
>
<title>
zobj2file
->
zblk2file
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
348.3313,-153.7196C333.1164,-142.1118 312.8112,-126.6204 296.0317,-113.8189
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
297.6975,-110.6875 287.6241,-107.4045 293.4515,-116.2528 297.6975,-110.68
75"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
566.0834,-173.1833C601.2772,-158.2151 654.873,-135.4203 693.4229,-119.0247
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
695.0468,-122.1375 702.8792,-115.0028 692.3071,-115.6959 695.0468,-122.13
75"
/>
</g>
<!-- zbtree2file -->
<g
id=
"node1
6
"
class=
"node"
>
<g
id=
"node1
9
"
class=
"node"
>
<title>
zbtree2file
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
475.8112"
cy=
"-90
"
rx=
"109.6807"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
475.8112"
y=
"-86.3
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
BTree/Bucket → file/[]#blk
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
529.8112"
cy=
"-98.8701
"
rx=
"109.6807"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
529.8112"
y=
"-95.1701
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
BTree/Bucket → file/[]#blk
</text>
</g>
<!-- zobj2file->zbtree2file -->
<g
id=
"edge1
6
"
class=
"edge"
>
<g
id=
"edge1
9
"
class=
"edge"
>
<title>
zobj2file
->
zbtree2file
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
393.079,-153.7196C408.0478,-142.1908 427.9906,-126.831 444.5456,-114.0805
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
447.0627,-116.5596 452.8496,-107.6848 442.7914,-111.0138 447.0627,-116.5596
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
529.8112,-170.4499C529.8112,-158.043 529.8112,-141.3523 529.8112,-127.209
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
533.3113,-127.0033 529.8112,-117.0033 526.3113,-127.0033 533.3113,-127.0033
"
/>
</g>
<!-- δBTree -->
<g
id=
"node
17
"
class=
"node"
>
<g
id=
"node
20
"
class=
"node"
>
<title>
δBTree
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
475
.8112"
cy=
"-18"
rx=
"43.5923"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
475
.8112"
y=
"-14.3"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
δ(BTree)
</text>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
529
.8112"
cy=
"-18"
rx=
"43.5923"
ry=
"18"
/>
<text
text-anchor=
"middle"
x=
"
529
.8112"
y=
"-14.3"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
δ(BTree)
</text>
</g>
<!-- zbtree2file->δBTree -->
<g
id=
"edge
17
"
class=
"edge"
>
<g
id=
"edge
20
"
class=
"edge"
>
<title>
zbtree2file
->
δBTree
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
475.8112,-71.8314C475.8112,-64.131 475.8112,-54.9743 475.8112,-46.4166
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
479.3113,-46.4132 475.8112,-36.4133 472.3113,-46.4133 479.3113,-46.4132
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M
529.8112,-80.5194C529.8112,-70.4898 529.8112,-57.8453 529.8112,-46.5565
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
533.3113,-46.278 529.8112,-36.278 526.3113,-46.2781 533.3113,-46.278
"
/>
</g>
<!-- clientInvHandle->headInv -->
<g
id=
"edge2
2
"
class=
"edge"
>
<g
id=
"edge2
5
"
class=
"edge"
>
<title>
clientInvHandle
->
headInv
</title>
<path
fill=
"none"
stroke=
"#000000"
d=
"M1
101.372,-234.6536C1111.8272,-222.6247 1124.2118,-208.3761 1134.6208,-196.4004
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1
137.5449,-198.3714 1141.4634,-188.5279 1132.2617,-193.7793 1137.5449,-198.3714
"
/>
<path
fill=
"none"
stroke=
"#000000"
d=
"M1
260.372,-252.3937C1270.8272,-240.3648 1283.2118,-226.1162 1293.6208,-214.1405
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1
296.5449,-216.1116 1300.4634,-206.268 1291.2617,-211.5194 1296.5449,-216.1116
"
/>
</g>
</g>
</svg>
wcfs/wcfs.go
View file @
9a66005f
...
...
@@ -224,44 +224,45 @@ package main
// Wcfs organization
//
// Wcfs is a ZODB client that translates ZODB objects into OS files as would
// non-wcfs wendelin.core do for a ZBigFile. It is organized as follows:
// non-wcfs wendelin.core do for a ZBigFile. Contrary to non-wcfs wendelin.core
// it keeps bigfile data in shared cache efficiently. It is organized as follows:
//
//
-
1 ZODB connection for "latest data" for whole filesystem (zconn).
//
-
head/data of all bigfiles represent state as of zconn.At .
//
-
for */head/data the following invariant is maintained:
//
1)
1 ZODB connection for "latest data" for whole filesystem (zconn).
//
2)
head/data of all bigfiles represent state as of zconn.At .
//
3)
for */head/data the following invariant is maintained:
//
// #blk ∈ file cache => ZBlk(#blk) + all BTree/Bucket that lead to it ∈ zconn cache
// (ZBlk* in ghost state)
//
// The invariant helps on invalidation: if we see a changed oid, and
// zconn.cache.lookup(oid) = ø -> we know we don't have to invalidate OS
// cache for any part of any file (even if oid relates to a file block - that
// block is not cached and will trigger ZODB load on file read).
//
The invariant helps on invalidation: if we see a changed oid, and
//
zconn.cache.lookup(oid) = ø -> we know we don't have to invalidate OS
//
cache for any part of any file (even if oid relates to a file block - that
//
block is not cached and will trigger ZODB load on file read).
//
// Currently we maintain this invariant by simply never evicting LOBTree/LOBucket
// objects from ZODB Connection cache (LOBucket keeps references to ZBlk* and
// so ZBlk* also stay in cache in ghost form). In the future we may want to
// try to synchronize to kernel freeing its pagecache pages.
//
Currently we maintain this invariant by simply never evicting LOBTree/LOBucket
//
objects from ZODB Connection cache (LOBucket keeps references to ZBlk* and
//
so ZBlk* also stay in cache in ghost form). In the future we may want to
//
try to synchronize to kernel freeing its pagecache pages.
//
//
-
when we receive an invalidation message from zstor - we process it and
// propagate invalidations to OS file cache of */head/data:
//
4)
when we receive an invalidation message from zstor - we process it and
//
propagate invalidations to OS file cache of */head/data:
//
// invalidation message: (tid↑, []oid)
//
//
1.
zconn.cache.lookup(oid)
//
2.
ø: nothing to do - see invariant ^^^.
//
3.
obj found:
//
4.1)
zconn.cache.lookup(oid)
//
4.2)
ø: nothing to do - see invariant ^^^.
//
4.3)
obj found:
//
// - ZBlk* -> file/#blk
// - BTree/Bucket -> δ(BTree) -> file/[]#blk
//
//
in the end after processing all []oid from invalidation message we have
//
in the end after processing all []oid from invalidation message we have
//
// [] of file/[]#blk
//
//
that describes which file(s) parts needs to be invalidated.
//
that describes which file(s) parts needs to be invalidated.
//
// 4. for all file/blk to invalidate we do:
// 4.
4)
for all file/blk to invalidate we do:
//
// - try to retrieve file/head/data[blk] from OS file cache;
// - if retrieved successfully -> store retrieved data back into OS file
...
...
@@ -276,22 +277,24 @@ package main
// won't be served from OS file cache and instead will trigger a FUSE read
// request to wcfs.
//
//
-
for every file δFtail invalidation info about head/data is maintained:
//
5)
for every file δFtail invalidation info about head/data is maintained:
//
// - tail: of [](rev↑, []#blk)
// - by: {} #blk -> []rev↑ in tail
//
//
δFtail.tail describes invalidations to file we learned from ZODB invalidation.
//
δFtail.by allows to quickly lookup information by #blk.
// δFtail.tail describes invalidations to file we learned from ZODB invalidation.
// δFtail.by allows to quickly lookup information by #blk.
//
//
min(rev) in δFtail is min(@at) at which head/data is currently mmapped (see below).
// min(rev) in δFtail is min(@at) at which head/data is currently mmapped (see below).
//
//
- when we receive a FUSE read(#blk) request to a file/head/data we process it as follows:
//
TODO invariant -> δFtail - which entries are present?
//
//
1. load blkdata for head/data[blk] @zconn.at .
//
6) when we receive a FUSE read(#blk) request to a file/head/data we process it as follows:
//
// while loading this also gives upper bound estimate of when the block
// was last changed:
// 6.1) load blkdata for head/data[blk] @zconn.at .
//
// while loading this also gives upper bound estimate of when the block
// was last changed:
//
// rev(blk) ≤ max(_.serial for _ in (ZBlk(#blk), all BTree/Bucket that lead to ZBlk))
//
...
...
@@ -312,9 +315,9 @@ package main
// rev(blk) ≤ rev'(blk) rev'(blk) = min(^^^)
//
//
//
2.
for all client/addr@at mmappings of file/head/data:
//
6.2)
for all client/addr@at mmappings of file/head/data:
//
// - rev'(blk) ≤ at: -> do nothing
// - rev'(blk) ≤ at: -> do nothing
XXX || blk ∉ mapping
// - rev'(blk) > at:
// - if blk ∈ mmapping.pinned -> do nothing
// - rev = max(δFtail.by(#blk) : _ ≤ at) || at
...
...
@@ -331,7 +334,7 @@ package main
//
// is maintained.
//
//
3.
blkdata is returned to kernel.
//
6.3)
blkdata is returned to kernel.
//
// Thus a client that wants latest data on pagefault will get latest data,
// and a client that wants @rev data will get @rev data, even if it was this
...
...
@@ -353,7 +356,7 @@ package main
// of view - initial open of a file this way would be potentially very slow.
// -> we took the approach where we invalidate lazily only actual block access.
//
// XXX(integrate place=?) ZData - n
e
need to keep track -> ZBlk1 is always
// XXX(integrate place=?) ZData - n
o
need to keep track -> ZBlk1 is always
// marked as changed on blk data change.
//
// ----------------------------------------
...
...
@@ -453,7 +456,7 @@ import (
"lab.nexedi.com/kirr/go123/xcontext"
"lab.nexedi.com/kirr/go123/xerr"
"lab.nexedi.com/kirr/neo/go/transaction"
//
"lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/kirr/neo/go/zodb/btree"
_
"lab.nexedi.com/kirr/neo/go/zodb/wks"
...
...
@@ -463,10 +466,30 @@ import (
"github.com/pkg/errors"
)
// Root represents root of wcfs filesystem.
type
Root
struct
{
nodefs
.
Node
// ZODB storage we work with
zstor
zodb
.
IStorage
// ZODB DB handle for zstor.
// keeps cache of connections for both head/ and @<rev>/ accesses.
//
// only one connection is used for head/ and only one for each @<rev>. XXX
zdb
*
zodb
.
DB
// ZODB connection for head/
zconn
*
ZConn
// ZODB connections for @<rev>/
zrevMu
sync
.
Mutex
zrevTab
map
[
zodb
.
Tid
]
*
ZConn
}
// /bigfile/ - served by BigFileRoot.
type
BigFileRoot
struct
{
nodefs
.
Node
zstor
zodb
.
IStorage
// {} oid -> <bigfileX>/
mu
sync
.
Mutex
...
...
@@ -478,10 +501,6 @@ type BigFileDir struct {
nodefs
.
Node
oid
zodb
.
Oid
// oid of ZBigFile
// ZODB DB handle for this bigfile.
// keeps cache of connections for both head/ and @<rev>/ accesses.
zdb
*
zodb
.
DB
// head/ is implicitly linked to by fs
// {} rev -> @<rev>/ bigfile snapshot
...
...
@@ -497,12 +516,8 @@ type BigFileRev struct {
// /bigfile/<bigfileX>/(head|<rev>)/* - internally served by BigFile.
type
BigFile
struct
{
// current read-only transaction under which we access ZODB data
txnCtx
context
.
Context
// XXX -> better directly store txn
// connection via which ZODB objects for this bigfile are accessed
// XXX do we need to keep it here explicitly?
zconn
*
zodb
.
Connection
// this BigFile view ZODB via zconn
zconn
*
ZConn
// ZBigFile top-level object. Kept activated during lifetime of current transaction.
zbf
*
ZBigFile
...
...
@@ -510,7 +525,7 @@ type BigFile struct {
// zbf.Size(). It is constant during liftime of current transaction.
zbfSize
int64
// TODO
// TODO
-> δFtail
// lastChange zodb.Tid // last change to whole bigfile as of .zconn.At view
}
...
...
@@ -526,6 +541,9 @@ type BigFileData struct {
// XXX -> BigFile ?
loadMu
sync
.
Mutex
loading
map
[
int64
]
*
blkLoadState
// #blk -> {... blkdata}
// XXX mappings where client(s) requested isolation guarantee
//mappings ...
}
// blkLoadState represents a ZBlk load state/result.
...
...
@@ -539,6 +557,84 @@ type blkLoadState struct {
err
error
}
// ----------------------------------------
/*
// XXX invalidation watcher
// XXX naming
func (sb *Super) main(ctx context.Context) error {
// XXX err ctx
// XXX unmount on error? -> always EIO?
znotify := sb.zconn.Notifier()
for {
zevent, err := znotify.Read(ctx)
if err != nil {
return err
}
// XXX locking
sb.zhandle1(zevent)
}
}
// zhandle1 handles 1 event from ZODB notification.
// XXX something is locked
func (sb *Super) zhandle1(zevent *zodb.NotifyEvent) {
toinvalidate = ... // [] of file/[]#blk
// zevent = (tid^, []oid)
for _, oid := zevent.Oidv {
obj := zconn.Cache().Get(oid)
if obj == nil {
continue // nothing to do - see invariant
}
switch obj := obj.(type) {
default:
continue // object not related to a bigfile
case *LOBTree:
// XXX -> δBTree
case *LOBucket:
// XXX -> δBTree
case *ZBlkXXXAny:
fileinv := XXX(obj.file)
fileinv.blkv += obj.blk // XXX or better obj.blkv ?
}
}
wg = ...
for _, fileinv := range toinvalidate {
for _, blk := range fileinv.blkv {
wg.go fileinv.file.invalidateBlk(blk)
}
}
// invalidateBlk invalidates 1 file block. XXX
// XXX see "4. for all file/blk to in invalidate we do"
func (f *file) invalidateBlk(ctx context.Context, blk int64) error {
fsconn := f.super().fsconn
off := blk*blksize
// try retrieve cache of current head/data[blk]
blkdata, st := fsconn.FileRetrieveCache(f.Inode(), off, blksize)
// XXX st != ok, len(blkdata) < blksize
// store retrieved data back to OS cache for file @<rev>/data[blk]
frev = ... // XXX
st = fsconn.FileNotifyStoreCache(frev.Inode(), off, blkdata)
// XXX st != ok
st = fsconn.FileNotify(f.Inode(), off, blksize)
// XXX st != ok (fatal here)
}
*/
// ----------------------------------------
...
...
@@ -569,8 +665,7 @@ func (bfroot *BigFileRoot) mkdir(name string, fctx *fuse.Context) (_ *nodefs.Ino
}
// not there - without bfroot lock proceed to open BigFile from ZODB
zdb
:=
zodb
.
NewDB
(
bfroot
.
zstor
)
bf
,
err
:=
bigopen
(
asctx
(
fctx
),
zdb
,
oid
,
&
zodb
.
ConnOptions
{})
bf
,
err
:=
bigopen
(
asctx
(
fctx
),
groot
.
zconn
,
oid
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -592,7 +687,6 @@ func (bfroot *BigFileRoot) mkdir(name string, fctx *fuse.Context) (_ *nodefs.Ino
bfdir
:=
&
BigFileDir
{
Node
:
nodefs
.
NewDefaultNode
(),
oid
:
oid
,
zdb
:
zdb
,
revTab
:
make
(
map
[
zodb
.
Tid
]
*
BigFileRev
),
}
...
...
@@ -651,9 +745,14 @@ func (bfdir *BigFileDir) mkdir(name string, fctx *fuse.Context) (_ *nodefs.Inode
}
// not there - without bfdir lock proceed to open BigFile @tid view of ZODB
bf
,
err
:=
bigopen
(
asctx
(
fctx
),
bfdir
.
zdb
,
bfdir
.
oid
,
&
zodb
.
ConnOptions
{
At
:
tid
,
})
ctx
:=
asctx
(
fctx
)
zconnRev
,
err
:=
groot
.
zopenAt
(
ctx
,
tid
)
if
err
!=
nil
{
return
nil
,
err
}
defer
zconnRev
.
Release
()
bf
,
err
:=
bigopen
(
ctx
,
zconnRev
,
bfdir
.
oid
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -693,36 +792,18 @@ func (bfdir *BigFileDir) mkdir(name string, fctx *fuse.Context) (_ *nodefs.Inode
}
// bigopen opens BigFile corresponding to oid
and zopt
.
// bigopen opens BigFile corresponding to oid
on zconn
.
//
// A new read-only transaction is opened.
// A new ZODB connection is opened via zdb.
// A ZBigFile corresponding to oid is activated and statted.
//
// The whole result is returned as BigFile.
func
bigopen
(
ctx
context
.
Context
,
zdb
*
zodb
.
DB
,
oid
zodb
.
Oid
,
zopt
*
zodb
.
ConnOptions
)
(
_
*
BigFile
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"bigopen %s %s"
,
oid
,
zopt
)
// create new read-only transaction for this bigfile
txn
,
txnCtx
:=
transaction
.
New
(
context
.
Background
())
defer
func
()
{
if
err
!=
nil
{
txn
.
Abort
()
}
}()
// create new DB/Connection for this bigfile open
func
bigopen
(
ctx
context
.
Context
,
zconn
*
ZConn
,
oid
zodb
.
Oid
)
(
_
*
BigFile
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"bigopen %s @%s"
,
oid
,
zconn
.
At
())
// XXX better ctx = transaction.PutIntoContext(ctx, txn)
ctx
,
cancel
:=
xcontext
.
Merge
(
ctx
,
txnCtx
)
ctx
,
cancel
:=
xcontext
.
Merge
(
ctx
,
zconn
.
txnCtx
)
defer
cancel
()
zconn
,
err
:=
zdb
.
Open
(
ctx
,
zopt
)
if
err
!=
nil
{
return
nil
,
err
}
xzbf
,
err
:=
zconn
.
Get
(
ctx
,
oid
)
if
err
!=
nil
{
switch
errors
.
Cause
(
err
)
.
(
type
)
{
...
...
@@ -756,8 +837,8 @@ func bigopen(ctx context.Context, zdb *zodb.DB, oid zodb.Oid, zopt *zodb.ConnOpt
return
nil
,
err
}
zconn
.
Incref
()
return
&
BigFile
{
txnCtx
:
txnCtx
,
zconn
:
zconn
,
zbf
:
zbf
,
zbfSize
:
zbfSize
,
...
...
@@ -769,7 +850,7 @@ func (bf *BigFile) Close() error {
bf
.
zbf
.
PDeactivate
()
bf
.
zbf
=
nil
transaction
.
Current
(
bf
.
txnCtx
)
.
Abort
()
bf
.
zconn
.
Release
()
bf
.
zconn
=
nil
return
nil
...
...
@@ -824,7 +905,7 @@ func (bfdata *BigFileData) Read(_ nodefs.File, dest []byte, off int64, fctx *fus
dest
=
make
([]
byte
,
aend
-
aoff
)
// ~> [aoff:aend) in file
// XXX better ctx = transaction.PutIntoContext(ctx, txn)
ctx
,
cancel
:=
xcontext
.
Merge
(
asctx
(
fctx
),
bf
.
txnCtx
)
ctx
,
cancel
:=
xcontext
.
Merge
(
asctx
(
fctx
),
bf
.
zconn
.
txnCtx
)
defer
cancel
()
// read/load all block(s) in parallel
...
...
@@ -850,6 +931,8 @@ func (bfdata *BigFileData) Read(_ nodefs.File, dest []byte, off int64, fctx *fus
// readBlk serves Read to read 1 ZBlk #blk into destination buffer.
//
// see "6) when we receive a FUSE read(#blk) request ..." in overview.
//
// len(dest) == blksize.
func
(
bfdata
*
BigFileData
)
readBlk
(
ctx
context
.
Context
,
blk
int64
,
dest
[]
byte
)
error
{
// XXX errctx?
...
...
@@ -882,11 +965,40 @@ func (bfdata *BigFileData) readBlk(ctx context.Context, blk int64, dest []byte)
// noone was loading - we became reponsible to load this block
zbf
:=
bfdata
.
bigfile
.
zbf
blkdata
,
err
:=
zbf
.
LoadBlk
(
ctx
,
blk
)
blkdata
,
err
:=
zbf
.
LoadBlk
(
ctx
,
blk
)
// XXX -> +blkrevmax1
loading
.
blkdata
=
blkdata
loading
.
err
=
err
close
(
loading
.
ready
)
// XXX before loading.ready?
/*
blkrevmax2 := max(δFtail.by(blk)) || min(rev for rev in δFtail)
revmax = min(blkrevmax1, blkrevmax2)
// XXX remmapping
// XXX -> own func?
// XXX locking
for _, mapping := range bfdata.mappings {
if revmax <= mapping.at || !mapping.blkrange.in(blk) {
continue // do nothing
}
if mapping.pinned.Contains(blk) {
continue // do nothing
}
rev = max(δFtail.by(blk) : _ <= mapping.at)
// XXX vvv -> go
client.remmap(mapping.addr[blk], file/@<rev>/data)
mapping.pinned.Add(blk)
}
*/
// data loaded with error - cleanup .loading
if
loading
.
err
!=
nil
{
bfdata
.
loadMu
.
Lock
()
...
...
@@ -940,8 +1052,8 @@ func (bfdata *BigFileData) readBlk(ctx context.Context, blk int64, dest []byte)
// /bigfile/<bigfileX>/head/at -> readAt serves read.
func
(
bf
*
BigFile
)
readAt
()
[]
byte
{
// XXX locking
// XXX z
conn -> zbf.PJar()
?
return
[]
byte
(
bf
.
z
conn
.
At
()
.
String
())
// XXX z
bf.PJar() not good if we want to share objects between connections
?
return
[]
byte
(
bf
.
z
bf
.
PJar
()
.
At
()
.
String
())
}
...
...
@@ -976,12 +1088,13 @@ func (cc *zodbCacheControl) WantEvict(obj zodb.IPersistent) bool {
return
false
}
// FIXME gfsconn is tmp workaround for lack of way to retrieve FileSystemConnector from nodefs.Inode
// FIXME g
root/g
fsconn is tmp workaround for lack of way to retrieve FileSystemConnector from nodefs.Inode
// TODO:
// - Inode += .Mount() -> nodefs.Mount
// - Mount:
// .Root() -> root Inode of the fs
// .Connector() -> FileSystemConnector through which fs is mounted
var
groot
*
Root
var
gfsconn
*
nodefs
.
FileSystemConnector
func
main
()
{
...
...
@@ -1005,7 +1118,7 @@ func main() {
stdlog
.
SetFlags
(
stdlog
.
Lmicroseconds
)
}
// open zodb storage
// open zodb storage
/db/connection
ctx
:=
context
.
Background
()
// XXX + timeout?
zstor
,
err
:=
zodb
.
OpenStorage
(
ctx
,
zurl
,
&
zodb
.
OpenOptions
{
ReadOnly
:
true
})
if
err
!=
nil
{
...
...
@@ -1013,7 +1126,21 @@ func main() {
}
defer
zstor
.
Close
()
zdb
:=
zodb
.
NewDB
(
zstor
)
zconn
,
err
:=
zopen
(
ctx
,
zdb
,
&
zodb
.
ConnOptions
{})
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
// mount root
root
:=
&
Root
{
Node
:
nodefs
.
NewDefaultNode
(),
zstor
:
zstor
,
zdb
:
zdb
,
zconn
:
zconn
,
zrevTab
:
make
(
map
[
zodb
.
Tid
]
*
ZConn
),
}
opts
:=
&
fuse
.
MountOptions
{
FsName
:
zurl
,
Name
:
"wcfs"
,
...
...
@@ -1022,12 +1149,12 @@ func main() {
Debug
:
*
debug
,
}
root
:=
nodefs
.
NewDefaultNode
()
fssrv
,
fsconn
,
err
:=
mount
(
mntpt
,
root
,
opts
)
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
gfsconn
=
fsconn
// FIXME temp workaround (see ^^^)
groot
=
root
// FIXME temp workaround (see ^^^)
gfsconn
=
fsconn
// FIXME ----//----
// we require proper pagecache control (added to Linux 2.6.36 in 2010)
supports
:=
fssrv
.
KernelSettings
()
.
SupportsNotify
...
...
@@ -1039,7 +1166,6 @@ func main() {
mkfile
(
root
,
".wcfs"
,
NewStaticFile
([]
byte
(
zurl
)))
mkdir
(
root
,
"bigfile"
,
&
BigFileRoot
{
Node
:
nodefs
.
NewDefaultNode
(),
zstor
:
zstor
,
tab
:
make
(
map
[
zodb
.
Oid
]
*
BigFileDir
),
})
...
...
wcfs/wcfs_test.py
View file @
9a66005f
...
...
@@ -201,7 +201,7 @@ def test_bigfile_empty():
# sync wcfs to ZODB
wc
.
_sync
()
# we wrote "hello world" after
10
th block, but size is always mutiple of blksize.
# we wrote "hello world" after
hole'
th block, but size is always mutiple of blksize.
fsize
=
(
hole
+
1
)
*
blksize
st
=
os
.
stat
(
fpath
+
"/head/data"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment