Skip to content

Commit

Permalink
feat: wip on iradix
Browse files Browse the repository at this point in the history
  • Loading branch information
tigerwill90 committed Nov 19, 2024
1 parent e83afcf commit f1eb0bb
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 54 deletions.
39 changes: 31 additions & 8 deletions fox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,19 @@ func BenchmarkStaticAll(b *testing.B) {
benchRoutes(b, r, staticRoutes)
}

func BenchmarkInsertStatic(b *testing.B) {
f := New()

b.ResetTimer()
b.ReportAllocs()

for range b.N {
for _, route := range staticRoutes {
f.Handle(route.method, route.path, emptyHandler)
}
}
}

func BenchmarkInsertStaticTx(b *testing.B) {
f := New()

Expand Down Expand Up @@ -5559,19 +5572,20 @@ func TestRaceHostnamePathSwitch(t *testing.T) {

f := New()

h := func(c Context) {}

require.NoError(t, f.Updates(func(txn *Txn) error {
for _, rte := range githubAPI {
if err := onlyError(txn.Handle(rte.method, rte.path, func(c Context) {
fmt.Println(c.Pattern())
})); err != nil {
if err := onlyError(txn.Handle(rte.method, rte.path, h)); err != nil {
return err
}
}
return nil
}))

wg.Add(1000 * 2)
wg.Add(1000 * 3)
for range 1000 {

go func() {
wait()
defer wg.Done()
Expand All @@ -5586,13 +5600,18 @@ func TestRaceHostnamePathSwitch(t *testing.T) {
}

for _, rte := range githubAPI {
if err := onlyError(txn.Handle(rte.method, "{sub}.bar.{tld}"+rte.path, emptyHandler)); err != nil {
if err := onlyError(txn.Handle(rte.method, "{sub}.bar.{tld}"+rte.path, h)); err != nil {
return err
}
}
return nil
}))

}()

go func() {
wait()
defer wg.Done()
require.NoError(t, f.Updates(func(txn *Txn) error {
if txn.Has(githubAPI[0].method, "foo.bar.baz"+githubAPI[0].path) {
for _, rte := range githubAPI {
Expand All @@ -5604,7 +5623,7 @@ func TestRaceHostnamePathSwitch(t *testing.T) {
}

for _, rte := range githubAPI {
if err := onlyError(txn.Handle(rte.method, "foo.bar.baz"+rte.path, emptyHandler)); err != nil {
if err := onlyError(txn.Handle(rte.method, "foo.bar.baz"+rte.path, h)); err != nil {
return err
}
}
Expand All @@ -5631,8 +5650,12 @@ func TestRaceHostnamePathSwitch(t *testing.T) {
start()
wg.Wait()

/* tree := f.getRoot()
require.Len(t, tree.root[0].children, 1)*/
// With a pair number of iteration, we should always delete all domains
tree := f.getRoot()
for _, n := range tree.root {
assert.Len(t, n.children, 1)
}

}

func TestDataRace(t *testing.T) {
Expand Down
5 changes: 2 additions & 3 deletions iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ func (it *rawIterator) hasNext() bool {
}

if len(elem.children) > 0 {
// TODO probably elem.children is now OKAY for read only
it.stack = append(it.stack, stack{edges: elem.getEdges()})
it.stack = append(it.stack, stack{edges: elem.children})
}

it.current = elem
Expand Down Expand Up @@ -173,7 +172,7 @@ func (it Iter) Prefix(methods iter.Seq[string], prefix string) iter.Seq2[string,
}

if len(elem.children) > 0 {
stacks = append(stacks, stack{edges: elem.getEdges()})
stacks = append(stacks, stack{edges: elem.children})
}

if elem.isLeaf() {
Expand Down
9 changes: 3 additions & 6 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,12 +370,6 @@ Walk:
continue
}

// path: GET
// path: /foo/*{any}/ [any (11)]
// path: b
// path: /c/bbb/ [leaf=/foo/*{any}/b/c/bbb/]
// path: bb [leaf=/foo/*{any}/bbb]
// path: c/bbb/ [leaf=/foo/*{any}/c/bbb/]
if current.key[i] == starDelim {
// | current.params[paramKeyCnt].end (10)
// key: foo/*{bar}/ => 10 - 5 = 5 => i+=idx set i to '/'
Expand Down Expand Up @@ -653,6 +647,7 @@ type node struct {
wildcardChildIndex int
}

// newNode create a new node. Note that is sort in place children, so it should NEVER be a slice from reference.
func newNode(key string, route *Route, children []*node) *node {
slices.SortFunc(children, func(a, b *node) int {
return cmp.Compare(a.key, b.key)
Expand Down Expand Up @@ -733,9 +728,11 @@ func (n *node) updateEdge(node *node) {
n.children[id] = node
}

// clone returns a copy of the nodes.
func (n *node) clone() *node {
children := make([]*node, len(n.children))
copy(children, n.children)
// We need to recalculate inode.
return newNodeFromRef(n.key, n.route, children, n.childKeys, n.paramChildIndex, n.wildcardChildIndex)
}

Expand Down
59 changes: 31 additions & 28 deletions tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func (t *txn) clone() *txn {
return tx
}

// insert is not safe for concurrent use
func (t *txn) insert(method string, route *Route, paramsN uint32) error {
if t.writable == nil {
lru, err := simplelru.NewLRU[*node, any](defaultModifiedCache, nil)
Expand Down Expand Up @@ -303,6 +304,7 @@ func (t *txn) insert(method string, route *Route, paramsN uint32) error {
return nil
}

// update is not safe for concurrent use
func (t *txn) update(method string, route *Route) error {
if t.writable == nil {
lru, err := simplelru.NewLRU[*node, any](defaultModifiedCache, nil)
Expand Down Expand Up @@ -561,69 +563,69 @@ func (t *txn) truncate(methods []string) {
// This effectively allow to create a fully isolated snapshot of the tree.
// Note: This function should be guarded by mutex.
func (t *txn) updateToRoot(p, pp, ppp *node, visited []*node, n *node) {
nn := n
last := n
if p != nil {
np := p
if _, ok := t.writable.Get(np); !ok {
np = np.clone()
t.writable.Add(np, nil)
// np is root and has never been writen
pc := p
if _, ok := t.writable.Get(pc); !ok {
pc = pc.clone()
t.writable.Add(pc, nil)
// pc is root and has never been writen
if pp == nil {
np.updateEdge(n)
t.updateRoot(np)
pc.updateEdge(n)
t.updateRoot(pc)
return
}
}

// If it's a clone, it's not a root
np.updateEdge(n)
pc.updateEdge(n)
if pp == nil {
return
}
nn = np
last = pc
}

if pp != nil {
npp := pp
if _, ok := t.writable.Get(npp); !ok {
npp = npp.clone()
t.writable.Add(npp, nil)
// npp is root and has never been writen
ppc := pp
if _, ok := t.writable.Get(ppc); !ok {
ppc = ppc.clone()
t.writable.Add(ppc, nil)
// ppc is root and has never been writen
if ppp == nil {
npp.updateEdge(nn)
t.updateRoot(npp)
ppc.updateEdge(last)
t.updateRoot(ppc)
return
}
}

// If it's a clone, it's not a root
npp.updateEdge(nn)
ppc.updateEdge(last)
if ppp == nil {
return
}
nn = npp
last = ppc
}

nppp := ppp
if _, ok := t.writable.Get(nppp); !ok {
nppp = nppp.clone()
t.writable.Add(nppp, nil)
// nppp is root and has never been writen
pppc := ppp
if _, ok := t.writable.Get(pppc); !ok {
pppc = pppc.clone()
t.writable.Add(pppc, nil)
// pppc is root and has never been writen
if len(visited) == 0 {
nppp.updateEdge(nn)
t.updateRoot(nppp)
pppc.updateEdge(last)
t.updateRoot(pppc)
return
}
}

// If it's a clone, it's not a root
nppp.updateEdge(nn)
pppc.updateEdge(last)
if len(visited) == 0 {
return
}

// Propagate update to the root node
current := nppp
current := pppc
for i := len(visited) - 1; i >= 0; i-- {
vNode := visited[i]

Expand Down Expand Up @@ -666,6 +668,7 @@ func commonPrefix(k1, k2 string) string {
return k1[:minLength]
}

// recreateParentEdge returns a copy of parent children, minus the matched node.
func recreateParentEdge(parent, matched *node) []*node {
parentEdges := make([]*node, len(parent.children)-1)
added := 0
Expand Down
20 changes: 11 additions & 9 deletions txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,18 @@ func TestTxn_TruncateAll(t *testing.T) {
func TestX(t *testing.T) {
f := New()

f.MustHandle(http.MethodGet, "/foo/bar", func(c Context) {

f.Updates(func(txn *Txn) error {
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ab", emptyHandler)))
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ab/cd", emptyHandler)))
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ab/cd/ef", emptyHandler)))
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ab/cd/ef/gh", emptyHandler)))
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ab/cd/ef/gh/ij", emptyHandler)))
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ab/c", emptyHandler)))
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ax/bba", emptyHandler)))
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ax/cbb", emptyHandler)))
require.NoError(t, onlyError(txn.Handle(http.MethodGet, "/ax/", emptyHandler)))
return nil
})
tree := f.getRoot()
fmt.Println(tree.root[0])

f.MustHandle(http.MethodGet, "/foo/baz", func(c Context) {

})

tree = f.getRoot()
fmt.Println(tree.root[0])
}

0 comments on commit f1eb0bb

Please sign in to comment.