Skip to content

Commit fb1b3e8

Browse files
committed
use cache
1 parent 3982ada commit fb1b3e8

File tree

7 files changed

+147
-15
lines changed

7 files changed

+147
-15
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ just use your client to request. it servers on stdio
1919
## Todo
2020

2121
- localCache
22-
- [ ] use a local cache to avoid search every time(From reddit user)
22+
- [x] use a local cache to avoid search every time(From reddit user)
2323
- searchPackage
2424
- [x] imported by how many packages
2525
- getPackageInfo

cmd/godoc-mcp-server/search.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import (
1313
func getSearchTool() (tool mcp.Tool, handler server.ToolHandlerFunc) {
1414
return mcp.NewTool("searchPackages",
1515
mcp.WithDescription("provide a query, search related golang packages from pkg.go.dev include "+
16-
"name, path, synopsis, go doc url, imported by how many packages, subpackages in this package"),
16+
"name, path, synopsis, go doc url, imported by how many packages, subpackages in this package "+
17+
"the path is the package full name. if want to use getPackageInfo. llm should pass the path as "+
18+
"packageName to getPackageInfo"),
1719
mcp.WithString("q",
1820
mcp.Required(),
1921
mcp.Description("query")),

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.24.2
44

55
require (
66
github.com/PuerkitoBio/goquery v1.10.2
7+
github.com/coocood/freecache v1.2.4
78
github.com/go-resty/resty/v2 v2.16.5
89
github.com/mark3labs/mcp-go v0.18.0
910
github.com/pkg/errors v0.9.1
@@ -13,6 +14,7 @@ require (
1314

1415
require (
1516
github.com/andybalholm/cascadia v1.3.3 // indirect
17+
github.com/cespare/xxhash/v2 v2.1.2 // indirect
1618
github.com/google/uuid v1.6.0 // indirect
1719
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
1820
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9Hq
22
github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU=
33
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
44
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
5+
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
6+
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
7+
github.com/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M=
8+
github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk=
59
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
610
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
711
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=

pkg/godoc/cache.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package godoc
2+
3+
import (
4+
"bytes"
5+
"compress/gzip"
6+
"log"
7+
"sync"
8+
9+
"github.com/coocood/freecache"
10+
"github.com/pkg/errors"
11+
)
12+
13+
// cacheSize 50M
14+
var cacheSize = 50 * 1024 * 1024
15+
16+
// cacheTTL600s 10 minutes
17+
var cacheTTL600s = 600
18+
19+
var cache = sync.OnceValue(func() *freecache.Cache {
20+
c := freecache.NewCache(cacheSize)
21+
22+
return c
23+
})
24+
25+
func getWithFn(key string, fn func() ([]byte, error)) ([]byte, error) {
26+
v, exist, err := get(key)
27+
if err != nil {
28+
return v, err
29+
}
30+
if exist {
31+
return v, nil
32+
}
33+
// not exist
34+
v, err = fn()
35+
if err != nil {
36+
return v, err
37+
}
38+
39+
go func() {
40+
defer func() {
41+
if t := recover(); t != nil {
42+
log.Println("recovered from ", t)
43+
}
44+
}()
45+
data, err := compress(v)
46+
if err != nil {
47+
log.Println(err)
48+
return
49+
}
50+
51+
// set to cache
52+
err = cache().Set([]byte(key), data, cacheTTL600s)
53+
if errors.Is(err, freecache.ErrLargeEntry) {
54+
log.Print("large entry for key", key)
55+
return
56+
}
57+
if err != nil {
58+
log.Println(err)
59+
}
60+
}()
61+
62+
return v, nil
63+
}
64+
65+
func get(key string) ([]byte, bool, error) {
66+
data, err := cache().Get([]byte(key))
67+
if errors.Is(err, freecache.ErrNotFound) {
68+
return nil, false, nil
69+
}
70+
if err != nil {
71+
return nil, false, errors.WithStack(err)
72+
}
73+
data, err = decompress(data)
74+
if err != nil {
75+
return nil, false, errors.WithStack(err)
76+
}
77+
78+
return data, true, nil
79+
}
80+
81+
func compress(data []byte) ([]byte, error) {
82+
var b bytes.Buffer
83+
gz, err := gzip.NewWriterLevel(&b, gzip.BestCompression)
84+
if err != nil {
85+
return nil, err
86+
}
87+
_, err = gz.Write(data)
88+
if err != nil {
89+
return nil, err
90+
}
91+
err = gz.Close()
92+
if err != nil {
93+
return nil, err
94+
}
95+
return b.Bytes(), nil
96+
}
97+
98+
func decompress(data []byte) ([]byte, error) {
99+
b := bytes.NewBuffer(data)
100+
gz, err := gzip.NewReader(b)
101+
if err != nil {
102+
return nil, err
103+
}
104+
defer gz.Close()
105+
var res bytes.Buffer
106+
_, err = res.ReadFrom(gz)
107+
if err != nil {
108+
return nil, err
109+
}
110+
return res.Bytes(), nil
111+
}

pkg/godoc/package.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,20 @@ type GetPackageRequest struct {
6262
}
6363

6464
func GetPackageDocument(req GetPackageRequest) (*PackageDocument, error) {
65-
resp, err := client().
66-
R().
67-
Get(baseURL() + "/" + req.PackageName)
65+
body, err := getWithFn(req.PackageName, func() ([]byte, error) {
66+
resp, err := client().
67+
R().
68+
Get(baseURL() + "/" + req.PackageName)
69+
if err != nil {
70+
return nil, errors.WithStack(err)
71+
}
72+
return resp.Body(), nil
73+
})
6874
if err != nil {
69-
return nil, errors.WithStack(err)
75+
return nil, err
7076
}
71-
result, err := extractDocResult(resp.String(), req)
77+
78+
result, err := extractDocResult(string(body), req)
7279
if err != nil {
7380
return nil, err
7481
}

pkg/godoc/search.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,23 @@ type SearchPackageInfo struct {
2424
}
2525

2626
func Search(query string) (*SearchResult, error) {
27-
resp, err := client().R().
28-
SetQueryParams(map[string]string{
29-
"q": query,
30-
"m": "package",
31-
}).
32-
Get(baseURL() + "/search")
27+
body, err := getWithFn(query, func() ([]byte, error) {
28+
resp, err := client().R().
29+
SetQueryParams(map[string]string{
30+
"q": query,
31+
"m": "package",
32+
}).
33+
Get(baseURL() + "/search")
34+
if err != nil {
35+
return nil, errors.WithStack(err)
36+
}
37+
return resp.Body(), nil
38+
})
3339
if err != nil {
34-
return nil, errors.WithStack(err)
40+
return nil, err
3541
}
3642

37-
return extractSearchResult(resp.String())
43+
return extractSearchResult(string(body))
3844
}
3945

4046
func extractSearchResult(query string) (*SearchResult, error) {

0 commit comments

Comments
 (0)