Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
dms3
go-merkledag
Commits
9eb769d7
Commit
9eb769d7
authored
Jan 07, 2017
by
Jeromy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
rewrite enumerate children async to be less fragile
License: MIT Signed-off-by:
Jeromy
<
why@ipfs.io
>
parent
49557764
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
109 additions
and
77 deletions
+109
-77
merkledag.go
merkledag.go
+66
-77
merkledag_test.go
merkledag_test.go
+43
-0
No files found.
merkledag.go
View file @
9eb769d7
...
@@ -389,103 +389,92 @@ func EnumerateChildren(ctx context.Context, ds LinkService, root *cid.Cid, visit
...
@@ -389,103 +389,92 @@ func EnumerateChildren(ctx context.Context, ds LinkService, root *cid.Cid, visit
return
nil
return
nil
}
}
func
EnumerateChildrenAsync
(
ctx
context
.
Context
,
ds
DAGService
,
c
*
cid
.
Cid
,
visit
func
(
*
cid
.
Cid
)
bool
)
error
{
// FetchGraphConcurrency is total number of concurrent fetches that
toprocess
:=
make
(
chan
[]
*
cid
.
Cid
,
8
)
// 'fetchNodes' will start at a time
nodes
:=
make
(
chan
*
NodeOption
,
8
)
var
FetchGraphConcurrency
=
8
ctx
,
cancel
:=
context
.
WithCancel
(
ctx
)
defer
cancel
()
defer
close
(
toprocess
)
go
fetchNodes
(
ctx
,
ds
,
toprocess
,
nodes
)
func
EnumerateChildrenAsync
(
ctx
context
.
Context
,
ds
DAGService
,
c
*
cid
.
Cid
,
visit
func
(
*
cid
.
Cid
)
bool
)
error
{
if
!
visit
(
c
)
{
return
nil
}
root
,
err
:=
ds
.
Get
(
ctx
,
c
)
root
,
err
:=
ds
.
Get
(
ctx
,
c
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
nodes
<-
&
NodeOption
{
Node
:
root
}
feed
:=
make
(
chan
node
.
Node
)
live
:=
1
out
:=
make
(
chan
*
NodeOption
)
done
:=
make
(
chan
struct
{})
for
{
var
setlk
sync
.
Mutex
select
{
case
opt
,
ok
:=
<-
nodes
:
if
!
ok
{
return
nil
}
if
opt
.
Err
!=
nil
{
return
opt
.
Err
}
nd
:=
opt
.
Node
// a node has been fetched
for
i
:=
0
;
i
<
FetchGraphConcurrency
;
i
++
{
live
--
go
func
()
{
for
n
:=
range
feed
{
var
cids
[]
*
cid
.
Cid
links
:=
n
.
Links
()
for
_
,
lnk
:=
range
nd
.
Links
()
{
cids
:=
make
([]
*
cid
.
Cid
,
0
,
len
(
links
))
c
:=
lnk
.
Cid
for
_
,
l
:=
range
links
{
if
visit
(
c
)
{
setlk
.
Lock
()
live
++
unseen
:=
visit
(
l
.
Cid
)
cids
=
append
(
cids
,
c
)
setlk
.
Unlock
()
}
if
unseen
{
cids
=
append
(
cids
,
l
.
Cid
)
}
}
if
live
==
0
{
return
nil
}
}
if
len
(
cids
)
>
0
{
for
nopt
:=
range
ds
.
GetMany
(
ctx
,
cids
)
{
select
{
select
{
case
toprocess
<-
cids
:
case
out
<-
nopt
:
case
<-
ctx
.
Done
()
:
case
<-
ctx
.
Done
()
:
return
ctx
.
Err
()
return
}
}
}
}
select
{
case
done
<-
struct
{}{}
:
case
<-
ctx
.
Done
()
:
case
<-
ctx
.
Done
()
:
return
ctx
.
Err
()
}
}
}
}
}
// FetchGraphConcurrency is total number of concurrenct fetches that
// 'fetchNodes' will start at a time
var
FetchGraphConcurrency
=
8
func
fetchNodes
(
ctx
context
.
Context
,
ds
DAGService
,
in
<-
chan
[]
*
cid
.
Cid
,
out
chan
<-
*
NodeOption
)
{
var
wg
sync
.
WaitGroup
defer
func
()
{
// wait for all 'get' calls to complete so we don't accidentally send
// on a closed channel
wg
.
Wait
()
close
(
out
)
}()
}()
}
defer
close
(
feed
)
rateLimit
:=
make
(
chan
struct
{},
FetchGraphConcurrency
)
send
:=
feed
var
todobuffer
[]
node
.
Node
var
inProgress
int
get
:=
func
(
ks
[]
*
cid
.
Cid
)
{
next
:=
root
defer
wg
.
Done
()
for
{
defer
func
()
{
<-
rateLimit
}()
nodes
:=
ds
.
GetMany
(
ctx
,
ks
)
for
opt
:=
range
nodes
{
select
{
select
{
case
out
<-
opt
:
case
send
<-
next
:
case
<-
ctx
.
Done
()
:
inProgress
++
return
if
len
(
todobuffer
)
>
0
{
next
=
todobuffer
[
0
]
todobuffer
=
todobuffer
[
1
:
]
}
else
{
next
=
nil
send
=
nil
}
}
case
<-
done
:
inProgress
--
if
inProgress
==
0
&&
next
==
nil
{
return
nil
}
}
case
nc
:=
<-
out
:
if
nc
.
Err
!=
nil
{
return
nc
.
Err
}
if
next
==
nil
{
next
=
nc
.
Node
send
=
feed
}
else
{
todobuffer
=
append
(
todobuffer
,
nc
.
Node
)
}
}
for
ks
:=
range
in
{
select
{
case
rateLimit
<-
struct
{}{}
:
case
<-
ctx
.
Done
()
:
case
<-
ctx
.
Done
()
:
return
return
ctx
.
Err
()
}
}
wg
.
Add
(
1
)
go
get
(
ks
)
}
}
}
}
merkledag_test.go
View file @
9eb769d7
...
@@ -504,3 +504,46 @@ func TestCidRawDoesnNeedData(t *testing.T) {
...
@@ -504,3 +504,46 @@ func TestCidRawDoesnNeedData(t *testing.T) {
t
.
Fatal
(
"raw node shouldn't have any links"
)
t
.
Fatal
(
"raw node shouldn't have any links"
)
}
}
}
}
func
TestEnumerateAsyncFailsNotFound
(
t
*
testing
.
T
)
{
a
:=
NodeWithData
([]
byte
(
"foo1"
))
b
:=
NodeWithData
([]
byte
(
"foo2"
))
c
:=
NodeWithData
([]
byte
(
"foo3"
))
d
:=
NodeWithData
([]
byte
(
"foo4"
))
ds
:=
dstest
.
Mock
()
for
_
,
n
:=
range
[]
node
.
Node
{
a
,
b
,
c
}
{
_
,
err
:=
ds
.
Add
(
n
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
}
parent
:=
new
(
ProtoNode
)
if
err
:=
parent
.
AddNodeLinkClean
(
"a"
,
a
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
err
:=
parent
.
AddNodeLinkClean
(
"b"
,
b
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
err
:=
parent
.
AddNodeLinkClean
(
"c"
,
c
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
err
:=
parent
.
AddNodeLinkClean
(
"d"
,
d
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
pcid
,
err
:=
ds
.
Add
(
parent
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
cset
:=
cid
.
NewSet
()
err
=
EnumerateChildrenAsync
(
context
.
Background
(),
ds
,
pcid
,
cset
.
Visit
)
if
err
==
nil
{
t
.
Fatal
(
"this should have failed"
)
}
}
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