Commit 9a66005f authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent c71be2d4
......@@ -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)
}
}
......@@ -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]
......
......@@ -4,264 +4,303 @@
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
-->
<!-- Title: %3 Pages: 1 -->
<svg width="1290pt" height="385pt"
viewBox="0.00 0.00 1290.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 381.2203)">
<svg width="1449pt" height="403pt"
viewBox="0.00 0.00 1449.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 398.9605)">
<title>%3</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-381.2203 1286.4565,-381.2203 1286.4565,4 -4,4"/>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-398.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&#45;&gt;invProto -->
<g id="edge1" class="edge">
<title>wcfs&#45;&gt;invProto</title>
<path fill="none" stroke="#000000" d="M623.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="M782.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&#45;&gt;autoexit -->
<g id="edge3" class="edge">
<title>wcfs&#45;&gt;autoexit</title>
<path fill="none" stroke="#000000" d="M571.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="M730.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&#45;&gt;wcfsInvProcess -->
<g id="edge4" class="edge">
<title>wcfs&#45;&gt;wcfsInvProcess</title>
<path fill="none" stroke="#000000" d="M582.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="M741.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&#45;&gt;wcfsRead -->
<g id="edge5" class="edge">
<title>wcfs&#45;&gt;wcfsRead</title>
<path fill="none" stroke="#000000" d="M619.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.121"/>
<path fill="none" stroke="#000000" d="M778.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.8611"/>
</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&#45;&gt;client -->
<g id="edge2" class="edge">
<title>invProto&#45;&gt;client</title>
<path fill="none" stroke="#000000" d="M890.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="M1049.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&#45;&gt;wcfsRead -->
<g id="edge18" class="edge">
<g id="edge21" class="edge">
<title>client&#45;&gt;wcfsRead</title>
<path fill="none" stroke="#000000" d="M949.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="M1108.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="node13" class="node">
<g id="node16" class="node">
<title>mappingRegister</title>
<ellipse fill="none" stroke="#000000" cx="1000.8112" cy="-170.8701" rx="65.1077" ry="26.7407"/>
<text text-anchor="middle" x="1000.8112" y="-174.6701" font-family="Times,serif" font-size="14.00" fill="#000000">mmappings:</text>
<text text-anchor="middle" x="1000.8112" y="-159.6701" font-family="Times,serif" font-size="14.00" fill="#000000">register/maint</text>
<ellipse fill="none" stroke="#000000" cx="1159.8112" cy="-188.6102" rx="65.1077" ry="26.7407"/>
<text text-anchor="middle" x="1159.8112" y="-192.4102" font-family="Times,serif" font-size="14.00" fill="#000000">mmappings:</text>
<text text-anchor="middle" x="1159.8112" y="-177.4102" font-family="Times,serif" font-size="14.00" fill="#000000">register/maint</text>
</g>
<!-- client&#45;&gt;mappingRegister -->
<g id="edge19" class="edge">
<g id="edge22" class="edge">
<title>client&#45;&gt;mappingRegister</title>
<path fill="none" stroke="#000000" d="M972.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="M1131.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="node18" class="node">
<g id="node21" class="node">
<title>clientInvHandle</title>
<ellipse fill="none" stroke="#000000" cx="1078.8112" cy="-260.6102" rx="80.7205" ry="26.7407"/>
<text text-anchor="middle" x="1078.8112" y="-264.4102" font-family="Times,serif" font-size="14.00" fill="#000000">process</text>
<text text-anchor="middle" x="1078.8112" y="-249.4102" font-family="Times,serif" font-size="14.00" fill="#000000">#blk invalidations</text>
<ellipse fill="none" stroke="#000000" cx="1237.8112" cy="-278.3503" rx="80.7205" ry="26.7407"/>
<text text-anchor="middle" x="1237.8112" y="-282.1503" font-family="Times,serif" font-size="14.00" fill="#000000">process</text>
<text text-anchor="middle" x="1237.8112" y="-267.1503" font-family="Times,serif" font-size="14.00" fill="#000000">#blk invalidations</text>
</g>
<!-- client&#45;&gt;clientInvHandle -->
<g id="edge20" class="edge">
<g id="edge23" class="edge">
<title>client&#45;&gt;clientInvHandle</title>
<path fill="none" stroke="#000000" d="M987.8103,-335.5315C1002.0729,-323.7891 1022.5081,-306.9648 1040.4013,-292.2332"/>
<polygon fill="#000000" stroke="#000000" points="1042.9168,-294.6957 1048.4124,-285.6376 1038.4676,-289.2916 1042.9168,-294.6957"/>
<path fill="none" stroke="#000000" d="M1146.8103,-353.2716C1161.0729,-341.5292 1181.5081,-324.7049 1199.4013,-309.9733"/>
<polygon fill="#000000" stroke="#000000" points="1201.9168,-312.4359 1207.4124,-303.3777 1197.4676,-307.0317 1201.9168,-312.4359"/>
</g>
<!-- nowcfs -->
<g id="node19" class="node">
<g id="node22" class="node">
<title>nowcfs</title>
<ellipse fill="none" stroke="#000000" cx="1229.8112" cy="-260.6102" rx="52.7911" ry="18"/>
<text text-anchor="middle" x="1229.8112" y="-256.9102" font-family="Times,serif" font-size="14.00" fill="#000000">!wcfs mode</text>
<ellipse fill="none" stroke="#000000" cx="1388.8112" cy="-278.3503" rx="52.7911" ry="18"/>
<text text-anchor="middle" x="1388.8112" y="-274.6503" font-family="Times,serif" font-size="14.00" fill="#000000">!wcfs mode</text>
</g>
<!-- client&#45;&gt;nowcfs -->
<g id="edge21" class="edge">
<g id="edge24" class="edge">
<title>client&#45;&gt;nowcfs</title>
<path fill="none" stroke="#000000" d="M997.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="1190.6473,-282.4166 1198.4215,-275.2185 1187.8553,-275.9975 1190.6473,-282.4166"/>
<path fill="none" stroke="#000000" d="M1156.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="1349.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&#45;&gt;ZODB_go_inv -->
<g id="edge6" class="edge">
<title>wcfsInvProcess&#45;&gt;ZODB_go_inv</title>
<path fill="none" stroke="#000000" d="M459.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="M618.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&#45;&gt;zconnCacheGet -->
<g id="edge7" class="edge">
<title>wcfsInvProcess&#45;&gt;zconnCacheGet</title>
<path fill="none" stroke="#000000" d="M457.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="M616.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&#45;&gt;zobj2file -->
<g id="edge8" class="edge">
<title>wcfsInvProcess&#45;&gt;zobj2file</title>
<path fill="none" stroke="#000000" d="M482.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="M641.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&#45;&gt;δFtail -->
<g id="edge9" class="edge">
<title>wcfsInvProcess&#45;&gt;δFtail</title>
<path fill="none" stroke="#000000" d="M556.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="M715.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&#45;&gt;fuseRetrieveCache -->
<g id="edge10" class="edge">
<title>wcfsInvProcess&#45;&gt;fuseRetrieveCache</title>
<path fill="none" stroke="#000000" d="M523.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="M682.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&#45;&gt;δFtail -->
<g id="edge12" class="edge">
<g id="edge15" class="edge">
<title>wcfsRead&#45;&gt;δFtail</title>
<path fill="none" stroke="#000000" d="M798.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="M957.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="node12" class="node">
<g id="node15" 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&#45;&gt;blktabGet -->
<g id="edge11" class="edge">
<g id="edge14" class="edge">
<title>wcfsRead&#45;&gt;blktabGet</title>
<path fill="none" stroke="#000000" d="M827.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="M986.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&#45;&gt;mappingRegister -->
<g id="edge13" class="edge">
<g id="edge16" class="edge">
<title>wcfsRead&#45;&gt;mappingRegister</title>
<path fill="none" stroke="#000000" d="M861.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="M1020.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="node14" class="node">
<g id="node17" class="node">
<title>headInv</title>
<ellipse fill="none" stroke="#000000" cx="1156.8112" cy="-170.8701" rx="73.387" ry="18"/>
<text text-anchor="middle" x="1156.8112" y="-167.1701" font-family="Times,serif" font-size="14.00" fill="#000000">#blk ← head/inv.</text>
<ellipse fill="none" stroke="#000000" cx="1315.8112" cy="-188.6102" rx="73.387" ry="18"/>
<text text-anchor="middle" x="1315.8112" y="-184.9102" font-family="Times,serif" font-size="14.00" fill="#000000">#blk ← head/inv.</text>
</g>
<!-- wcfsRead&#45;&gt;headInv -->
<g id="edge14" class="edge">
<g id="edge17" class="edge">
<title>wcfsRead&#45;&gt;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&#45;&gt;fs1_go_inv -->
<g id="edge11" class="edge">
<title>ZODB_go_inv&#45;&gt;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&#45;&gt;zeo_go_inv -->
<g id="edge12" class="edge">
<title>ZODB_go_inv&#45;&gt;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&#45;&gt;neo_go_inv -->
<g id="edge13" class="edge">
<title>ZODB_go_inv&#45;&gt;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="node15" class="node">
<g id="node18" 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&#45;&gt;zblk2file -->
<g id="edge15" class="edge">
<g id="edge18" class="edge">
<title>zobj2file&#45;&gt;zblk2file</title>
<path fill="none" stroke="#000000" d="M348.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.6875"/>
<path fill="none" stroke="#000000" d="M566.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.1375"/>
</g>
<!-- zbtree2file -->
<g id="node16" class="node">
<g id="node19" 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&#45;&gt;zbtree2file -->
<g id="edge16" class="edge">
<g id="edge19" class="edge">
<title>zobj2file&#45;&gt;zbtree2file</title>
<path fill="none" stroke="#000000" d="M393.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="M529.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="node17" class="node">
<g id="node20" 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&#45;&gt;δBTree -->
<g id="edge17" class="edge">
<g id="edge20" class="edge">
<title>zbtree2file&#45;&gt;δBTree</title>
<path fill="none" stroke="#000000" d="M475.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="M529.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&#45;&gt;headInv -->
<g id="edge22" class="edge">
<g id="edge25" class="edge">
<title>clientInvHandle&#45;&gt;headInv</title>
<path fill="none" stroke="#000000" d="M1101.372,-234.6536C1111.8272,-222.6247 1124.2118,-208.3761 1134.6208,-196.4004"/>
<polygon fill="#000000" stroke="#000000" points="1137.5449,-198.3714 1141.4634,-188.5279 1132.2617,-193.7793 1137.5449,-198.3714"/>
<path fill="none" stroke="#000000" d="M1260.372,-252.3937C1270.8272,-240.3648 1283.2118,-226.1162 1293.6208,-214.1405"/>
<polygon fill="#000000" stroke="#000000" points="1296.5449,-216.1116 1300.4634,-206.268 1291.2617,-211.5194 1296.5449,-216.1116"/>
</g>
</g>
</svg>
......@@ -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 - ne need to keep track -> ZBlk1 is always
// XXX(integrate place=?) ZData - no 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 zconn -> zbf.PJar() ?
return []byte(bf.zconn.At().String())
// XXX zbf.PJar() not good if we want to share objects between connections?
return []byte(bf.zbf.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 groot/gfsconn 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),
})
......
......@@ -201,7 +201,7 @@ def test_bigfile_empty():
# sync wcfs to ZODB
wc._sync()
# we wrote "hello world" after 10th 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")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment