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
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Joshua
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
...
@@ -23,6 +23,7 @@ package main
import
(
import
(
"context"
"context"
"fmt"
"fmt"
"sync/atomic"
"syscall"
"syscall"
log
"github.com/golang/glog"
log
"github.com/golang/glog"
...
@@ -31,6 +32,9 @@ import (
...
@@ -31,6 +32,9 @@ import (
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/pkg/errors"
"github.com/pkg/errors"
"lab.nexedi.com/kirr/go123/xcontext"
"lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/kirr/neo/go/zodb"
)
)
...
@@ -163,6 +167,7 @@ func mount(mntpt string, root nodefs.Node, opts *fuse.MountOptions) (*fuse.Serve
...
@@ -163,6 +167,7 @@ func mount(mntpt string, root nodefs.Node, opts *fuse.MountOptions) (*fuse.Serve
return
fssrv
,
fsconn
,
nil
return
fssrv
,
fsconn
,
nil
}
}
// ---- ZODB ---
// typeOf returns ZODB type of an object.
// typeOf returns ZODB type of an object.
//
//
...
@@ -171,3 +176,140 @@ func mount(mntpt string, root nodefs.Node, opts *fuse.MountOptions) (*fuse.Serve
...
@@ -171,3 +176,140 @@ func mount(mntpt string, root nodefs.Node, opts *fuse.MountOptions) (*fuse.Serve
func
typeOf
(
obj
interface
{})
string
{
func
typeOf
(
obj
interface
{})
string
{
return
zodb
.
ClassOf
(
obj
)
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 {
...
@@ -25,6 +25,10 @@ digraph {
wcfsInvProcess
->
δ
Ftail
;
wcfsInvProcess
->
δ
Ftail
;
wcfsInvProcess
->
fuseRetrieveCache
;
wcfsInvProcess
->
fuseRetrieveCache
;
ZODB_go_inv
->
fs1_go_inv
;
ZODB_go_inv
->
zeo_go_inv
;
ZODB_go_inv
->
neo_go_inv
;
wcfsRead
->
blktabGet
;
wcfsRead
->
blktabGet
;
wcfsRead
->
δ
Ftail
;
wcfsRead
->
δ
Ftail
;
wcfsRead
->
mappingRegister
;
wcfsRead
->
mappingRegister
;
...
@@ -79,6 +83,9 @@ digraph {
...
@@ -79,6 +83,9 @@ digraph {
headInv
[
label
=
"#blk ← head/inv."
]
headInv
[
label
=
"#blk ← head/inv."
]
ZODB_go_inv
[
label
=
"ZODB/go\ninvalidations"
]
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]
// Btree_read [label="BTree read", style=filled fillcolor=lightyellow]
// ZBlk_read [label="ZBigFile / ZBlk* 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]
// ZODB_read [label="ZODB deserialize object", style=filled fillcolor=lightyellow]
...
...
wcfs/todo.svg
View file @
9a66005f
...
@@ -4,264 +4,303 @@
...
@@ -4,264 +4,303 @@
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
-->
-->
<!-- Title: %3 Pages: 1 -->
<!-- Title: %3 Pages: 1 -->
<svg
width=
"1
290pt"
height=
"385
pt"
<svg
width=
"1
449pt"
height=
"403
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"
>
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
81.2203
)"
>
<g
id=
"graph0"
class=
"graph"
transform=
"scale(1 1) rotate(0) translate(4 3
98.9605
)"
>
<title>
%3
</title>
<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 -->
<!-- wcfs -->
<g
id=
"node1"
class=
"node"
>
<g
id=
"node1"
class=
"node"
>
<title>
wcfs
</title>
<title>
wcfs
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
595.8112"
cy=
"-350.3503
"
rx=
"27.0966"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
754.8112"
cy=
"-368.0904
"
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>
<text
text-anchor=
"middle"
x=
"
754.8112"
y=
"-364.3904
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
wcfs
</text>
</g>
</g>
<!-- invProto -->
<!-- invProto -->
<g
id=
"node2"
class=
"node"
>
<g
id=
"node2"
class=
"node"
>
<title>
invProto
</title>
<title>
invProto
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
801.8112"
cy=
"-350.3503
"
rx=
"78.4777"
ry=
"26.7407"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
960.8112"
cy=
"-368.0904
"
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=
"
960.8112"
y=
"-371.8904
"
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>
<text
text-anchor=
"middle"
x=
"
960.8112"
y=
"-356.8904
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
protocol
</text>
</g>
</g>
<!-- wcfs->invProto -->
<!-- wcfs->invProto -->
<g
id=
"edge1"
class=
"edge"
>
<g
id=
"edge1"
class=
"edge"
>
<title>
wcfs
->
invProto
</title>
<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
"
/>
<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=
"
713.307,-353.8504 723.307,-350.3503 713.307,-346.8504 713.307,-353.8504
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
872.307,-371.5905 882.307,-368.0904 872.307,-364.5905 872.307,-371.5905
"
/>
</g>
</g>
<!-- autoexit -->
<!-- autoexit -->
<g
id=
"node4"
class=
"node"
>
<g
id=
"node4"
class=
"node"
>
<title>
autoexit
</title>
<title>
autoexit
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
363.8112"
cy=
"-260.6102
"
rx=
"52.1524"
ry=
"26.7407"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
522.8112"
cy=
"-278.3503
"
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=
"
522.8112"
y=
"-282.1503
"
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>
<text
text-anchor=
"middle"
x=
"
522.8112"
y=
"-267.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
if !activity
</text>
</g>
</g>
<!-- wcfs->autoexit -->
<!-- wcfs->autoexit -->
<g
id=
"edge3"
class=
"edge"
>
<g
id=
"edge3"
class=
"edge"
>
<title>
wcfs
->
autoexit
</title>
<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
"
/>
<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=
"
413.9322,-279.1855 403.3621,-278.4616 411.1737,-285.6191 413.9322,-279.1855
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
572.9322,-296.9256 562.3621,-296.2017 570.1737,-303.3592 572.9322,-296.9256
"
/>
</g>
</g>
<!-- wcfsInvProcess -->
<!-- wcfsInvProcess -->
<g
id=
"node5"
class=
"node"
>
<g
id=
"node5"
class=
"node"
>
<title>
wcfsInvProcess
</title>
<title>
wcfsInvProcess
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
523.8112"
cy=
"-260.6102
"
rx=
"89.6056"
ry=
"26.7407"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
682.8112"
cy=
"-278.3503
"
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=
"
682.8112"
y=
"-282.1503
"
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>
<text
text-anchor=
"middle"
x=
"
682.8112"
y=
"-267.1503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
ZODB invalidations
</text>
</g>
</g>
<!-- wcfs->wcfsInvProcess -->
<!-- wcfs->wcfsInvProcess -->
<g
id=
"edge4"
class=
"edge"
>
<g
id=
"edge4"
class=
"edge"
>
<title>
wcfs
->
wcfsInvProcess
</title>
<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
"
/>
<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=
"
554.0273,-292.6783 545.0393,-287.0687 548.5674,-297.0589 554.0273,-292.6783
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
713.0273,-310.4184 704.0393,-304.8088 707.5674,-314.799 713.0273,-310.4184
"
/>
</g>
</g>
<!-- wcfsRead -->
<!-- wcfsRead -->
<g
id=
"node6"
class=
"node"
>
<g
id=
"node6"
class=
"node"
>
<title>
wcfsRead
</title>
<title>
wcfsRead
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
833.8112"
cy=
"-260.6102
"
rx=
"47.3916"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
992.8112"
cy=
"-278.3503
"
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>
<text
text-anchor=
"middle"
x=
"
992.8112"
y=
"-274.6503
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
read(#blk)
</text>
</g>
</g>
<!-- wcfs->wcfsRead -->
<!-- wcfs->wcfsRead -->
<g
id=
"edge5"
class=
"edge"
>
<g
id=
"edge5"
class=
"edge"
>
<title>
wcfs
->
wcfsRead
</title>
<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
"
/>
<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=
"
791.9871,-280.121 800.1092,-273.3178 789.5173,-273.5711 791.9871,-280.12
1"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
950.9871,-297.8611 959.1092,-291.058 948.5173,-291.3112 950.9871,-297.861
1"
/>
</g>
</g>
<!-- client -->
<!-- client -->
<g
id=
"node3"
class=
"node"
>
<g
id=
"node3"
class=
"node"
>
<title>
client
</title>
<title>
client
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
969.8112"
cy=
"-350.3503
"
rx=
"30.5947"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
1128.8112"
cy=
"-368.0904
"
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>
<text
text-anchor=
"middle"
x=
"
1128.8112"
y=
"-364.3904
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
client
</text>
</g>
</g>
<!-- invProto->client -->
<!-- invProto->client -->
<g
id=
"edge2"
class=
"edge"
>
<g
id=
"edge2"
class=
"edge"
>
<title>
invProto
->
client
</title>
<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
"
/>
<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=
"
890.5612,-346.8504 880.5612,-350.3503 890.5611,-353.8504 890.5612,-346.8504
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
1049.5612,-364.5905 1039.5612,-368.0904 1049.5611,-371.5905 1049.5612,-364.5905
"
/>
</g>
</g>
<!-- client->wcfsRead -->
<!-- client->wcfsRead -->
<g
id=
"edge
18
"
class=
"edge"
>
<g
id=
"edge
21
"
class=
"edge"
>
<title>
client
->
wcfsRead
</title>
<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
"
/>
<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=
"
867.7919,-278.8392 857.5176,-276.2529 863.9366,-284.6819 867.7919,-278.8392
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
1026.7919,-296.5793 1016.5176,-293.993 1022.9366,-302.422 1026.7919,-296.5793
"
/>
</g>
</g>
<!-- mappingRegister -->
<!-- mappingRegister -->
<g
id=
"node1
3
"
class=
"node"
>
<g
id=
"node1
6
"
class=
"node"
>
<title>
mappingRegister
</title>
<title>
mappingRegister
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
000.8112"
cy=
"-170.8701
"
rx=
"65.1077"
ry=
"26.7407"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
159.8112"
cy=
"-188.6102
"
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
159.8112"
y=
"-192.4102
"
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>
<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>
</g>
<!-- client->mappingRegister -->
<!-- client->mappingRegister -->
<g
id=
"edge
19
"
class=
"edge"
>
<g
id=
"edge
22
"
class=
"edge"
>
<title>
client
->
mappingRegister
</title>
<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
"
/>
<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=
"
997.0681,-208.2434 995.5122,-197.7635 990.1931,-206.9263 997.0681,-208.2434
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
1156.0681,-225.9835 1154.5122,-215.5036 1149.1931,-224.6664 1156.0681,-225.9835
"
/>
</g>
</g>
<!-- clientInvHandle -->
<!-- clientInvHandle -->
<g
id=
"node
18
"
class=
"node"
>
<g
id=
"node
21
"
class=
"node"
>
<title>
clientInvHandle
</title>
<title>
clientInvHandle
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
078.8112"
cy=
"-260.6102
"
rx=
"80.7205"
ry=
"26.7407"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
237.8112"
cy=
"-278.3503
"
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
237.8112"
y=
"-282.1503
"
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>
<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>
</g>
<!-- client->clientInvHandle -->
<!-- client->clientInvHandle -->
<g
id=
"edge2
0
"
class=
"edge"
>
<g
id=
"edge2
3
"
class=
"edge"
>
<title>
client
->
clientInvHandle
</title>
<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
"
/>
<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
042.9168,-294.6957 1048.4124,-285.6376 1038.4676,-289.2916 1042.9168,-294.6957
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1
201.9168,-312.4359 1207.4124,-303.3777 1197.4676,-307.0317 1201.9168,-312.4359
"
/>
</g>
</g>
<!-- nowcfs -->
<!-- nowcfs -->
<g
id=
"node
19
"
class=
"node"
>
<g
id=
"node
22
"
class=
"node"
>
<title>
nowcfs
</title>
<title>
nowcfs
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
229.8112"
cy=
"-260.6102
"
rx=
"52.7911"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
388.8112"
cy=
"-278.3503
"
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>
<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>
</g>
<!-- client->nowcfs -->
<!-- client->nowcfs -->
<g
id=
"edge2
1
"
class=
"edge"
>
<g
id=
"edge2
4
"
class=
"edge"
>
<title>
client
->
nowcfs
</title>
<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
"
/>
<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
190.6473,-282.4166 1198.4215,-275.2185 1187.8553,-275.9975 1190.6473,-282.4166
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"1
349.6473,-300.1567 1357.4215,-292.9586 1346.8553,-293.7376 1349.6473,-300.1567
"
/>
</g>
</g>
<!-- ZODB_go_inv -->
<!-- ZODB_go_inv -->
<g
id=
"node7"
class=
"node"
>
<g
id=
"node7"
class=
"node"
>
<title>
ZODB_go_inv
</title>
<title>
ZODB_go_inv
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
60.8112"
cy=
"-170.8701
"
rx=
"60.623"
ry=
"26.7407"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
210.8112"
cy=
"-188.6102
"
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=
"
210.8112"
y=
"-192.4102
"
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>
<text
text-anchor=
"middle"
x=
"
210.8112"
y=
"-177.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
invalidations
</text>
</g>
</g>
<!-- wcfsInvProcess->ZODB_go_inv -->
<!-- wcfsInvProcess->ZODB_go_inv -->
<g
id=
"edge6"
class=
"edge"
>
<g
id=
"edge6"
class=
"edge"
>
<title>
wcfsInvProcess
->
ZODB_go_inv
</title>
<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
"
/>
<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=
"
115.3162,-189.5652 104.7214,-189.5974 113.0239,-196.1792 115.3162,-189.5652
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
267.7297,-205.8544 257.1388,-206.141 265.5968,-212.5216 267.7297,-205.8544
"
/>
</g>
</g>
<!-- zconnCacheGet -->
<!-- zconnCacheGet -->
<g
id=
"node8"
class=
"node"
>
<g
id=
"node8"
class=
"node"
>
<title>
zconnCacheGet
</title>
<title>
zconnCacheGet
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
210.8112"
cy=
"-170.8701
"
rx=
"71.4873"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
369.8112"
cy=
"-188.6102
"
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>
<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>
</g>
<!-- wcfsInvProcess->zconnCacheGet -->
<!-- wcfsInvProcess->zconnCacheGet -->
<g
id=
"edge7"
class=
"edge"
>
<g
id=
"edge7"
class=
"edge"
>
<title>
wcfsInvProcess
->
zconnCacheGet
</title>
<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
"
/>
<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=
"
263.7211,-185.2123 253.128,-185.404 261.5285,-191.8601 263.7211,-185.2123
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
422.7211,-202.9524 412.128,-203.1441 420.5285,-209.6002 422.7211,-202.9524
"
/>
</g>
</g>
<!-- zobj2file -->
<!-- zobj2file -->
<g
id=
"node9"
class=
"node"
>
<g
id=
"node9"
class=
"node"
>
<title>
zobj2file
</title>
<title>
zobj2file
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
370.8112"
cy=
"-170.8701
"
rx=
"70.3881"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
529.8112"
cy=
"-188.6102
"
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>
<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>
</g>
<!-- wcfsInvProcess->zobj2file -->
<!-- wcfsInvProcess->zobj2file -->
<g
id=
"edge8"
class=
"edge"
>
<g
id=
"edge8"
class=
"edge"
>
<title>
wcfsInvProcess
->
zobj2file
</title>
<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
"
/>
<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=
"
409.7252,-189.6369 399.3286,-187.5966 406.1836,-195.675 409.7252,-189.6369
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
568.7252,-207.3771 558.3286,-205.3367 565.1836,-213.4151 568.7252,-207.3771
"
/>
</g>
</g>
<!-- δFtail -->
<!-- δFtail -->
<g
id=
"node10"
class=
"node"
>
<g
id=
"node10"
class=
"node"
>
<title>
δFtail
</title>
<title>
δFtail
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
638.8112"
cy=
"-170.8701
"
rx=
"31.6951"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
797.8112"
cy=
"-188.6102
"
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>
<text
text-anchor=
"middle"
x=
"
797.8112"
y=
"-184.9102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
δFtail
</text>
</g>
</g>
<!-- wcfsInvProcess->δFtail -->
<!-- wcfsInvProcess->δFtail -->
<g
id=
"edge9"
class=
"edge"
>
<g
id=
"edge9"
class=
"edge"
>
<title>
wcfsInvProcess
->
δFtail
</title>
<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
"
/>
<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=
"
614.2507,-194.4754 619.9812,-185.564 609.9443,-188.9568 614.2507,-194.4754
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
773.2507,-212.2155 778.9812,-203.3041 768.9443,-206.6969 773.2507,-212.2155
"
/>
</g>
</g>
<!-- fuseRetrieveCache -->
<!-- fuseRetrieveCache -->
<g
id=
"node11"
class=
"node"
>
<g
id=
"node11"
class=
"node"
>
<title>
fuseRetrieveCache
</title>
<title>
fuseRetrieveCache
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
523.8112"
cy=
"-170.8701
"
rx=
"65.1077"
ry=
"26.7407"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
682.8112"
cy=
"-188.6102
"
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=
"
682.8112"
y=
"-192.4102
"
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>
<text
text-anchor=
"middle"
x=
"
682.8112"
y=
"-177.4102
"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
retrieve cache
</text>
</g>
</g>
<!-- wcfsInvProcess->fuseRetrieveCache -->
<!-- wcfsInvProcess->fuseRetrieveCache -->
<g
id=
"edge10"
class=
"edge"
>
<g
id=
"edge10"
class=
"edge"
>
<title>
wcfsInvProcess
->
fuseRetrieveCache
</title>
<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
"
/>
<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=
"
527.3113,-207.776 523.8112,-197.776 520.3113,-207.7761 527.3113,-207.776
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
686.3113,-225.5161 682.8112,-215.5162 679.3113,-225.5162 686.3113,-225.5161
"
/>
</g>
</g>
<!-- wcfsRead->δFtail -->
<!-- wcfsRead->δFtail -->
<g
id=
"edge1
2
"
class=
"edge"
>
<g
id=
"edge1
5
"
class=
"edge"
>
<title>
wcfsRead
->
δFtail
</title>
<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
"
/>
<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=
"
668.9424,-187.9238 658.6582,-185.3771 665.1096,-193.7813 668.9424,-187.9238
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
827.9424,-205.664 817.6582,-203.1172 824.1096,-211.5214 827.9424,-205.664
"
/>
</g>
</g>
<!-- blktabGet -->
<!-- blktabGet -->
<g
id=
"node1
2
"
class=
"node"
>
<g
id=
"node1
5
"
class=
"node"
>
<title>
blktabGet
</title>
<title>
blktabGet
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
802.8112"
cy=
"-170.8701
"
rx=
"114.6026"
ry=
"26.7407"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
961.8112"
cy=
"-188.6102
"
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=
"
961.8112"
y=
"-192.4102
"
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>
<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>
</g>
<!-- wcfsRead->blktabGet -->
<!-- wcfsRead->blktabGet -->
<g
id=
"edge1
1
"
class=
"edge"
>
<g
id=
"edge1
4
"
class=
"edge"
>
<title>
wcfsRead
->
blktabGet
</title>
<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
"
/>
<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=
"
818.7079,-206.1688 812.1345,-197.8597 812.0915,-208.4544 818.7079,-206.1688
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
977.7079,-223.9089 971.1345,-215.5998 971.0915,-226.1946 977.7079,-223.9089
"
/>
</g>
</g>
<!-- wcfsRead->mappingRegister -->
<!-- wcfsRead->mappingRegister -->
<g
id=
"edge1
3
"
class=
"edge"
>
<g
id=
"edge1
6
"
class=
"edge"
>
<title>
wcfsRead
->
mappingRegister
</title>
<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
"
/>
<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=
"
953.8541,-200.0766 961.0061,-192.2599 950.5406,-193.9104 953.8541,-200.0766
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
1112.8541,-217.8167 1120.0061,-210.0001 1109.5406,-211.6506 1112.8541,-217.8167
"
/>
</g>
</g>
<!-- headInv -->
<!-- headInv -->
<g
id=
"node1
4
"
class=
"node"
>
<g
id=
"node1
7
"
class=
"node"
>
<title>
headInv
</title>
<title>
headInv
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
156.8112"
cy=
"-170.8701
"
rx=
"73.387"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"1
315.8112"
cy=
"-188.6102
"
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>
<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>
</g>
<!-- wcfsRead->headInv -->
<!-- wcfsRead->headInv -->
<g
id=
"edge1
4
"
class=
"edge"
>
<g
id=
"edge1
7
"
class=
"edge"
>
<title>
wcfsRead
->
headInv
</title>
<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"
/>
<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=
"1105.1326,-192.0159 1113.5592,-185.5939 1102.9669,-185.3593 1105.1326,-192.0159"
/>
<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>
</g>
<!-- zblk2file -->
<!-- zblk2file -->
<g
id=
"node1
5
"
class=
"node"
>
<g
id=
"node1
8
"
class=
"node"
>
<title>
zblk2file
</title>
<title>
zblk2file
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
264.8112"
cy=
"-90
"
rx=
"83.3857"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
740.8112"
cy=
"-98.8701
"
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>
<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>
</g>
<!-- zobj2file->zblk2file -->
<!-- zobj2file->zblk2file -->
<g
id=
"edge1
5
"
class=
"edge"
>
<g
id=
"edge1
8
"
class=
"edge"
>
<title>
zobj2file
->
zblk2file
</title>
<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
"
/>
<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=
"
297.6975,-110.6875 287.6241,-107.4045 293.4515,-116.2528 297.6975,-110.68
75"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
695.0468,-122.1375 702.8792,-115.0028 692.3071,-115.6959 695.0468,-122.13
75"
/>
</g>
</g>
<!-- zbtree2file -->
<!-- zbtree2file -->
<g
id=
"node1
6
"
class=
"node"
>
<g
id=
"node1
9
"
class=
"node"
>
<title>
zbtree2file
</title>
<title>
zbtree2file
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
475.8112"
cy=
"-90
"
rx=
"109.6807"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
529.8112"
cy=
"-98.8701
"
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>
<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>
</g>
<!-- zobj2file->zbtree2file -->
<!-- zobj2file->zbtree2file -->
<g
id=
"edge1
6
"
class=
"edge"
>
<g
id=
"edge1
9
"
class=
"edge"
>
<title>
zobj2file
->
zbtree2file
</title>
<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
"
/>
<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=
"
447.0627,-116.5596 452.8496,-107.6848 442.7914,-111.0138 447.0627,-116.5596
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
533.3113,-127.0033 529.8112,-117.0033 526.3113,-127.0033 533.3113,-127.0033
"
/>
</g>
</g>
<!-- δBTree -->
<!-- δBTree -->
<g
id=
"node
17
"
class=
"node"
>
<g
id=
"node
20
"
class=
"node"
>
<title>
δBTree
</title>
<title>
δBTree
</title>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
475
.8112"
cy=
"-18"
rx=
"43.5923"
ry=
"18"
/>
<ellipse
fill=
"none"
stroke=
"#000000"
cx=
"
529
.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>
<text
text-anchor=
"middle"
x=
"
529
.8112"
y=
"-14.3"
font-family=
"Times,serif"
font-size=
"14.00"
fill=
"#000000"
>
δ(BTree)
</text>
</g>
</g>
<!-- zbtree2file->δBTree -->
<!-- zbtree2file->δBTree -->
<g
id=
"edge
17
"
class=
"edge"
>
<g
id=
"edge
20
"
class=
"edge"
>
<title>
zbtree2file
->
δBTree
</title>
<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
"
/>
<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=
"
479.3113,-46.4132 475.8112,-36.4133 472.3113,-46.4133 479.3113,-46.4132
"
/>
<polygon
fill=
"#000000"
stroke=
"#000000"
points=
"
533.3113,-46.278 529.8112,-36.278 526.3113,-46.2781 533.3113,-46.278
"
/>
</g>
</g>
<!-- clientInvHandle->headInv -->
<!-- clientInvHandle->headInv -->
<g
id=
"edge2
2
"
class=
"edge"
>
<g
id=
"edge2
5
"
class=
"edge"
>
<title>
clientInvHandle
->
headInv
</title>
<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
"
/>
<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
137.5449,-198.3714 1141.4634,-188.5279 1132.2617,-193.7793 1137.5449,-198.3714
"
/>
<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>
</g>
</g>
</svg>
</svg>
wcfs/wcfs.go
View file @
9a66005f
...
@@ -224,44 +224,45 @@ package main
...
@@ -224,44 +224,45 @@ package main
// Wcfs organization
// Wcfs organization
//
//
// Wcfs is a ZODB client that translates ZODB objects into OS files as would
// 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).
//
1)
1 ZODB connection for "latest data" for whole filesystem (zconn).
//
-
head/data of all bigfiles represent state as of zconn.At .
//
2)
head/data of all bigfiles represent state as of zconn.At .
//
-
for */head/data the following invariant is maintained:
//
3)
for */head/data the following invariant is maintained:
//
//
// #blk ∈ file cache => ZBlk(#blk) + all BTree/Bucket that lead to it ∈ zconn cache
// #blk ∈ file cache => ZBlk(#blk) + all BTree/Bucket that lead to it ∈ zconn cache
// (ZBlk* in ghost state)
// (ZBlk* in ghost state)
//
//
// The invariant helps on invalidation: if we see a changed oid, and
//
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
//
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
//
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).
//
block is not cached and will trigger ZODB load on file read).
//
//
// Currently we maintain this invariant by simply never evicting LOBTree/LOBucket
//
Currently we maintain this invariant by simply never evicting LOBTree/LOBucket
// objects from ZODB Connection cache (LOBucket keeps references to ZBlk* and
//
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
//
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.
//
try to synchronize to kernel freeing its pagecache pages.
//
//
//
-
when we receive an invalidation message from zstor - we process it and
//
4)
when we receive an invalidation message from zstor - we process it and
// propagate invalidations to OS file cache of */head/data:
//
propagate invalidations to OS file cache of */head/data:
//
//
// invalidation message: (tid↑, []oid)
// invalidation message: (tid↑, []oid)
//
//
//
1.
zconn.cache.lookup(oid)
//
4.1)
zconn.cache.lookup(oid)
//
2.
ø: nothing to do - see invariant ^^^.
//
4.2)
ø: nothing to do - see invariant ^^^.
//
3.
obj found:
//
4.3)
obj found:
//
//
// - ZBlk* -> file/#blk
// - ZBlk* -> file/#blk
// - BTree/Bucket -> δ(BTree) -> 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
// [] 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;
// - try to retrieve file/head/data[blk] from OS file cache;
// - if retrieved successfully -> store retrieved data back into OS file
// - if retrieved successfully -> store retrieved data back into OS file
...
@@ -276,22 +277,24 @@ package main
...
@@ -276,22 +277,24 @@ package main
// won't be served from OS file cache and instead will trigger a FUSE read
// won't be served from OS file cache and instead will trigger a FUSE read
// request to wcfs.
// 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)
// - tail: of [](rev↑, []#blk)
// - by: {} #blk -> []rev↑ in tail
// - by: {} #blk -> []rev↑ in tail
//
//
//
δFtail.tail describes invalidations to file we learned from ZODB invalidation.
// δFtail.tail describes invalidations to file we learned from ZODB invalidation.
//
δFtail.by allows to quickly lookup information by #blk.
// δ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
// 6.1) load blkdata for head/data[blk] @zconn.at .
// was last changed:
//
// 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))
// rev(blk) ≤ max(_.serial for _ in (ZBlk(#blk), all BTree/Bucket that lead to ZBlk))
//
//
...
@@ -312,9 +315,9 @@ package main
...
@@ -312,9 +315,9 @@ package main
// rev(blk) ≤ rev'(blk) rev'(blk) = min(^^^)
// 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:
// - rev'(blk) > at:
// - if blk ∈ mmapping.pinned -> do nothing
// - if blk ∈ mmapping.pinned -> do nothing
// - rev = max(δFtail.by(#blk) : _ ≤ at) || at
// - rev = max(δFtail.by(#blk) : _ ≤ at) || at
...
@@ -331,7 +334,7 @@ package main
...
@@ -331,7 +334,7 @@ package main
//
//
// is maintained.
// 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,
// 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
// and a client that wants @rev data will get @rev data, even if it was this
...
@@ -353,7 +356,7 @@ package main
...
@@ -353,7 +356,7 @@ package main
// of view - initial open of a file this way would be potentially very slow.
// 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.
// -> 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.
// marked as changed on blk data change.
//
//
// ----------------------------------------
// ----------------------------------------
...
@@ -453,7 +456,7 @@ import (
...
@@ -453,7 +456,7 @@ import (
"lab.nexedi.com/kirr/go123/xcontext"
"lab.nexedi.com/kirr/go123/xcontext"
"lab.nexedi.com/kirr/go123/xerr"
"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"
"lab.nexedi.com/kirr/neo/go/zodb/btree"
"lab.nexedi.com/kirr/neo/go/zodb/btree"
_
"lab.nexedi.com/kirr/neo/go/zodb/wks"
_
"lab.nexedi.com/kirr/neo/go/zodb/wks"
...
@@ -463,10 +466,30 @@ import (
...
@@ -463,10 +466,30 @@ import (
"github.com/pkg/errors"
"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.
// /bigfile/ - served by BigFileRoot.
type
BigFileRoot
struct
{
type
BigFileRoot
struct
{
nodefs
.
Node
nodefs
.
Node
zstor
zodb
.
IStorage
// {} oid -> <bigfileX>/
// {} oid -> <bigfileX>/
mu
sync
.
Mutex
mu
sync
.
Mutex
...
@@ -478,10 +501,6 @@ type BigFileDir struct {
...
@@ -478,10 +501,6 @@ type BigFileDir struct {
nodefs
.
Node
nodefs
.
Node
oid
zodb
.
Oid
// oid of ZBigFile
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
// head/ is implicitly linked to by fs
// {} rev -> @<rev>/ bigfile snapshot
// {} rev -> @<rev>/ bigfile snapshot
...
@@ -497,12 +516,8 @@ type BigFileRev struct {
...
@@ -497,12 +516,8 @@ type BigFileRev struct {
// /bigfile/<bigfileX>/(head|<rev>)/* - internally served by BigFile.
// /bigfile/<bigfileX>/(head|<rev>)/* - internally served by BigFile.
type
BigFile
struct
{
type
BigFile
struct
{
// current read-only transaction under which we access ZODB data
// this BigFile view ZODB via zconn
txnCtx
context
.
Context
// XXX -> better directly store txn
zconn
*
ZConn
// connection via which ZODB objects for this bigfile are accessed
// XXX do we need to keep it here explicitly?
zconn
*
zodb
.
Connection
// ZBigFile top-level object. Kept activated during lifetime of current transaction.
// ZBigFile top-level object. Kept activated during lifetime of current transaction.
zbf
*
ZBigFile
zbf
*
ZBigFile
...
@@ -510,7 +525,7 @@ type BigFile struct {
...
@@ -510,7 +525,7 @@ type BigFile struct {
// zbf.Size(). It is constant during liftime of current transaction.
// zbf.Size(). It is constant during liftime of current transaction.
zbfSize
int64
zbfSize
int64
// TODO
// TODO
-> δFtail
// lastChange zodb.Tid // last change to whole bigfile as of .zconn.At view
// lastChange zodb.Tid // last change to whole bigfile as of .zconn.At view
}
}
...
@@ -526,6 +541,9 @@ type BigFileData struct {
...
@@ -526,6 +541,9 @@ type BigFileData struct {
// XXX -> BigFile ?
// XXX -> BigFile ?
loadMu
sync
.
Mutex
loadMu
sync
.
Mutex
loading
map
[
int64
]
*
blkLoadState
// #blk -> {... blkdata}
loading
map
[
int64
]
*
blkLoadState
// #blk -> {... blkdata}
// XXX mappings where client(s) requested isolation guarantee
//mappings ...
}
}
// blkLoadState represents a ZBlk load state/result.
// blkLoadState represents a ZBlk load state/result.
...
@@ -539,6 +557,84 @@ type blkLoadState struct {
...
@@ -539,6 +557,84 @@ type blkLoadState struct {
err
error
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
...
@@ -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
// not there - without bfroot lock proceed to open BigFile from ZODB
zdb
:=
zodb
.
NewDB
(
bfroot
.
zstor
)
bf
,
err
:=
bigopen
(
asctx
(
fctx
),
groot
.
zconn
,
oid
)
bf
,
err
:=
bigopen
(
asctx
(
fctx
),
zdb
,
oid
,
&
zodb
.
ConnOptions
{})
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -592,7 +687,6 @@ func (bfroot *BigFileRoot) mkdir(name string, fctx *fuse.Context) (_ *nodefs.Ino
...
@@ -592,7 +687,6 @@ func (bfroot *BigFileRoot) mkdir(name string, fctx *fuse.Context) (_ *nodefs.Ino
bfdir
:=
&
BigFileDir
{
bfdir
:=
&
BigFileDir
{
Node
:
nodefs
.
NewDefaultNode
(),
Node
:
nodefs
.
NewDefaultNode
(),
oid
:
oid
,
oid
:
oid
,
zdb
:
zdb
,
revTab
:
make
(
map
[
zodb
.
Tid
]
*
BigFileRev
),
revTab
:
make
(
map
[
zodb
.
Tid
]
*
BigFileRev
),
}
}
...
@@ -651,9 +745,14 @@ func (bfdir *BigFileDir) mkdir(name string, fctx *fuse.Context) (_ *nodefs.Inode
...
@@ -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
// not there - without bfdir lock proceed to open BigFile @tid view of ZODB
bf
,
err
:=
bigopen
(
asctx
(
fctx
),
bfdir
.
zdb
,
bfdir
.
oid
,
&
zodb
.
ConnOptions
{
ctx
:=
asctx
(
fctx
)
At
:
tid
,
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
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -693,36 +792,18 @@ func (bfdir *BigFileDir) mkdir(name string, fctx *fuse.Context) (_ *nodefs.Inode
...
@@ -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.
// A ZBigFile corresponding to oid is activated and statted.
//
//
// The whole result is returned as BigFile.
// The whole result is returned as BigFile.
func
bigopen
(
ctx
context
.
Context
,
zdb
*
zodb
.
DB
,
oid
zodb
.
Oid
,
zopt
*
zodb
.
ConnOptions
)
(
_
*
BigFile
,
err
error
)
{
func
bigopen
(
ctx
context
.
Context
,
zconn
*
ZConn
,
oid
zodb
.
Oid
)
(
_
*
BigFile
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"bigopen %s %s"
,
oid
,
zopt
)
defer
xerr
.
Contextf
(
&
err
,
"bigopen %s @%s"
,
oid
,
zconn
.
At
())
// 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
// XXX better ctx = transaction.PutIntoContext(ctx, txn)
// XXX better ctx = transaction.PutIntoContext(ctx, txn)
ctx
,
cancel
:=
xcontext
.
Merge
(
ctx
,
txnCtx
)
ctx
,
cancel
:=
xcontext
.
Merge
(
ctx
,
zconn
.
txnCtx
)
defer
cancel
()
defer
cancel
()
zconn
,
err
:=
zdb
.
Open
(
ctx
,
zopt
)
if
err
!=
nil
{
return
nil
,
err
}
xzbf
,
err
:=
zconn
.
Get
(
ctx
,
oid
)
xzbf
,
err
:=
zconn
.
Get
(
ctx
,
oid
)
if
err
!=
nil
{
if
err
!=
nil
{
switch
errors
.
Cause
(
err
)
.
(
type
)
{
switch
errors
.
Cause
(
err
)
.
(
type
)
{
...
@@ -756,8 +837,8 @@ func bigopen(ctx context.Context, zdb *zodb.DB, oid zodb.Oid, zopt *zodb.ConnOpt
...
@@ -756,8 +837,8 @@ func bigopen(ctx context.Context, zdb *zodb.DB, oid zodb.Oid, zopt *zodb.ConnOpt
return
nil
,
err
return
nil
,
err
}
}
zconn
.
Incref
()
return
&
BigFile
{
return
&
BigFile
{
txnCtx
:
txnCtx
,
zconn
:
zconn
,
zconn
:
zconn
,
zbf
:
zbf
,
zbf
:
zbf
,
zbfSize
:
zbfSize
,
zbfSize
:
zbfSize
,
...
@@ -769,7 +850,7 @@ func (bf *BigFile) Close() error {
...
@@ -769,7 +850,7 @@ func (bf *BigFile) Close() error {
bf
.
zbf
.
PDeactivate
()
bf
.
zbf
.
PDeactivate
()
bf
.
zbf
=
nil
bf
.
zbf
=
nil
transaction
.
Current
(
bf
.
txnCtx
)
.
Abort
()
bf
.
zconn
.
Release
()
bf
.
zconn
=
nil
bf
.
zconn
=
nil
return
nil
return
nil
...
@@ -824,7 +905,7 @@ func (bfdata *BigFileData) Read(_ nodefs.File, dest []byte, off int64, fctx *fus
...
@@ -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
dest
=
make
([]
byte
,
aend
-
aoff
)
// ~> [aoff:aend) in file
// XXX better ctx = transaction.PutIntoContext(ctx, txn)
// 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
()
defer
cancel
()
// read/load all block(s) in parallel
// read/load all block(s) in parallel
...
@@ -850,6 +931,8 @@ func (bfdata *BigFileData) Read(_ nodefs.File, dest []byte, off int64, fctx *fus
...
@@ -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.
// 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.
// len(dest) == blksize.
func
(
bfdata
*
BigFileData
)
readBlk
(
ctx
context
.
Context
,
blk
int64
,
dest
[]
byte
)
error
{
func
(
bfdata
*
BigFileData
)
readBlk
(
ctx
context
.
Context
,
blk
int64
,
dest
[]
byte
)
error
{
// XXX errctx?
// XXX errctx?
...
@@ -882,11 +965,40 @@ func (bfdata *BigFileData) readBlk(ctx context.Context, blk int64, dest []byte)
...
@@ -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
// noone was loading - we became reponsible to load this block
zbf
:=
bfdata
.
bigfile
.
zbf
zbf
:=
bfdata
.
bigfile
.
zbf
blkdata
,
err
:=
zbf
.
LoadBlk
(
ctx
,
blk
)
blkdata
,
err
:=
zbf
.
LoadBlk
(
ctx
,
blk
)
// XXX -> +blkrevmax1
loading
.
blkdata
=
blkdata
loading
.
blkdata
=
blkdata
loading
.
err
=
err
loading
.
err
=
err
close
(
loading
.
ready
)
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
// data loaded with error - cleanup .loading
if
loading
.
err
!=
nil
{
if
loading
.
err
!=
nil
{
bfdata
.
loadMu
.
Lock
()
bfdata
.
loadMu
.
Lock
()
...
@@ -940,8 +1052,8 @@ func (bfdata *BigFileData) readBlk(ctx context.Context, blk int64, dest []byte)
...
@@ -940,8 +1052,8 @@ func (bfdata *BigFileData) readBlk(ctx context.Context, blk int64, dest []byte)
// /bigfile/<bigfileX>/head/at -> readAt serves read.
// /bigfile/<bigfileX>/head/at -> readAt serves read.
func
(
bf
*
BigFile
)
readAt
()
[]
byte
{
func
(
bf
*
BigFile
)
readAt
()
[]
byte
{
// XXX locking
// XXX locking
// XXX z
conn -> zbf.PJar()
?
// XXX z
bf.PJar() not good if we want to share objects between connections
?
return
[]
byte
(
bf
.
z
conn
.
At
()
.
String
())
return
[]
byte
(
bf
.
z
bf
.
PJar
()
.
At
()
.
String
())
}
}
...
@@ -976,12 +1088,13 @@ func (cc *zodbCacheControl) WantEvict(obj zodb.IPersistent) bool {
...
@@ -976,12 +1088,13 @@ func (cc *zodbCacheControl) WantEvict(obj zodb.IPersistent) bool {
return
false
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:
// TODO:
// - Inode += .Mount() -> nodefs.Mount
// - Inode += .Mount() -> nodefs.Mount
// - Mount:
// - Mount:
// .Root() -> root Inode of the fs
// .Root() -> root Inode of the fs
// .Connector() -> FileSystemConnector through which fs is mounted
// .Connector() -> FileSystemConnector through which fs is mounted
var
groot
*
Root
var
gfsconn
*
nodefs
.
FileSystemConnector
var
gfsconn
*
nodefs
.
FileSystemConnector
func
main
()
{
func
main
()
{
...
@@ -1005,7 +1118,7 @@ func main() {
...
@@ -1005,7 +1118,7 @@ func main() {
stdlog
.
SetFlags
(
stdlog
.
Lmicroseconds
)
stdlog
.
SetFlags
(
stdlog
.
Lmicroseconds
)
}
}
// open zodb storage
// open zodb storage
/db/connection
ctx
:=
context
.
Background
()
// XXX + timeout?
ctx
:=
context
.
Background
()
// XXX + timeout?
zstor
,
err
:=
zodb
.
OpenStorage
(
ctx
,
zurl
,
&
zodb
.
OpenOptions
{
ReadOnly
:
true
})
zstor
,
err
:=
zodb
.
OpenStorage
(
ctx
,
zurl
,
&
zodb
.
OpenOptions
{
ReadOnly
:
true
})
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -1013,7 +1126,21 @@ func main() {
...
@@ -1013,7 +1126,21 @@ func main() {
}
}
defer
zstor
.
Close
()
defer
zstor
.
Close
()
zdb
:=
zodb
.
NewDB
(
zstor
)
zconn
,
err
:=
zopen
(
ctx
,
zdb
,
&
zodb
.
ConnOptions
{})
if
err
!=
nil
{
log
.
Fatal
(
err
)
}
// mount root
// mount root
root
:=
&
Root
{
Node
:
nodefs
.
NewDefaultNode
(),
zstor
:
zstor
,
zdb
:
zdb
,
zconn
:
zconn
,
zrevTab
:
make
(
map
[
zodb
.
Tid
]
*
ZConn
),
}
opts
:=
&
fuse
.
MountOptions
{
opts
:=
&
fuse
.
MountOptions
{
FsName
:
zurl
,
FsName
:
zurl
,
Name
:
"wcfs"
,
Name
:
"wcfs"
,
...
@@ -1022,12 +1149,12 @@ func main() {
...
@@ -1022,12 +1149,12 @@ func main() {
Debug
:
*
debug
,
Debug
:
*
debug
,
}
}
root
:=
nodefs
.
NewDefaultNode
()
fssrv
,
fsconn
,
err
:=
mount
(
mntpt
,
root
,
opts
)
fssrv
,
fsconn
,
err
:=
mount
(
mntpt
,
root
,
opts
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Fatal
(
err
)
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)
// we require proper pagecache control (added to Linux 2.6.36 in 2010)
supports
:=
fssrv
.
KernelSettings
()
.
SupportsNotify
supports
:=
fssrv
.
KernelSettings
()
.
SupportsNotify
...
@@ -1039,7 +1166,6 @@ func main() {
...
@@ -1039,7 +1166,6 @@ func main() {
mkfile
(
root
,
".wcfs"
,
NewStaticFile
([]
byte
(
zurl
)))
mkfile
(
root
,
".wcfs"
,
NewStaticFile
([]
byte
(
zurl
)))
mkdir
(
root
,
"bigfile"
,
&
BigFileRoot
{
mkdir
(
root
,
"bigfile"
,
&
BigFileRoot
{
Node
:
nodefs
.
NewDefaultNode
(),
Node
:
nodefs
.
NewDefaultNode
(),
zstor
:
zstor
,
tab
:
make
(
map
[
zodb
.
Oid
]
*
BigFileDir
),
tab
:
make
(
map
[
zodb
.
Oid
]
*
BigFileDir
),
})
})
...
...
wcfs/wcfs_test.py
View file @
9a66005f
...
@@ -201,7 +201,7 @@ def test_bigfile_empty():
...
@@ -201,7 +201,7 @@ def test_bigfile_empty():
# sync wcfs to ZODB
# sync wcfs to ZODB
wc
.
_sync
()
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
fsize
=
(
hole
+
1
)
*
blksize
st
=
os
.
stat
(
fpath
+
"/head/data"
)
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