Skip to content
This repository was archived by the owner on Jul 2, 2024. It is now read-only.

Commit e498e06

Browse files
authored
Merge pull request #280 from ddvk/sync15concurrency
sync15: use parallel requests when getting remote tree
2 parents 28345f9 + de4d02a commit e498e06

13 files changed

+168
-75
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,4 @@ rMAPI will set the exit code to `0` if the command succeedes, or `1` if it fails
209209
- `RMAPI_AUTH`: override the default authorization url
210210
- `RMAPI_DOC`: override the default document storage url
211211
- `RMAPI_HOST`: override all urls
212+
- `RMAPI_CONCURRENT`: sync15: maximum number of goroutines/http requests to use (default: 20)

api/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type ApiCtx interface {
2424
DeleteEntry(node *model.Node) error
2525
SyncComplete() error
2626
Nuke() error
27+
Refresh() error
2728
}
2829

2930
type UserToken struct {

api/sync10/api.go

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ func (ctx *ApiCtx) Filetree() *filetree.FileTreeCtx {
2626
return ctx.ft
2727
}
2828

29+
func (ctx *ApiCtx) Refresh() (err error) {
30+
return errors.New("not implemented")
31+
}
32+
2933
// Nuke removes all documents from the account
3034
func (ctx *ApiCtx) Nuke() error {
3135
documents := make([]model.Document, 0)

api/sync15/apictx.go

+49-34
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import (
66
"errors"
77
"fmt"
88
"io"
9-
"io/ioutil"
109
"os"
10+
"strconv"
1111
"time"
1212

1313
"github.com/google/uuid"
@@ -19,8 +19,6 @@ import (
1919
"github.com/juruen/rmapi/util"
2020
)
2121

22-
var ErrorNotImplemented = errors.New("not implemented")
23-
2422
// An ApiCtx allows you interact with the remote reMarkable API
2523
type ApiCtx struct {
2624
Http *transport.HttpClientCtx
@@ -29,10 +27,45 @@ type ApiCtx struct {
2927
hashTree *HashTree
3028
}
3129

30+
// max number of concurrent requests
31+
var concurrent = 20
32+
33+
func init() {
34+
c := os.Getenv("RMAPI_CONCURRENT")
35+
if u, err := strconv.Atoi(c); err == nil {
36+
concurrent = u
37+
}
38+
}
39+
40+
func CreateCtx(http *transport.HttpClientCtx) (*ApiCtx, error) {
41+
apiStorage := NewBlobStorage(http)
42+
cacheTree, err := loadTree()
43+
if err != nil {
44+
fmt.Print(err)
45+
return nil, err
46+
}
47+
err = cacheTree.Mirror(apiStorage, concurrent)
48+
if err != nil {
49+
return nil, err
50+
}
51+
saveTree(cacheTree)
52+
tree := DocumentsFileTree(cacheTree)
53+
return &ApiCtx{http, tree, apiStorage, cacheTree}, nil
54+
}
55+
3256
func (ctx *ApiCtx) Filetree() *filetree.FileTreeCtx {
3357
return ctx.ft
3458
}
3559

60+
func (ctx *ApiCtx) Refresh() error {
61+
err := ctx.hashTree.Mirror(ctx.blobStorage, concurrent)
62+
if err != nil {
63+
return err
64+
}
65+
ctx.ft = DocumentsFileTree(ctx.hashTree)
66+
return nil
67+
}
68+
3669
// Nuke removes all documents from the account
3770
func (ctx *ApiCtx) Nuke() (err error) {
3871
err = Sync(ctx.blobStorage, ctx.hashTree, func(t *HashTree) error {
@@ -54,7 +87,7 @@ func (ctx *ApiCtx) FetchDocument(docId, dstPath string) error {
5487
return err
5588
}
5689

57-
tmp, err := ioutil.TempFile("", "rmapizip")
90+
tmp, err := os.CreateTemp("", "rmapizip")
5891

5992
if err != nil {
6093
log.Error.Println("failed to create tmpfile for zip dir", err)
@@ -104,7 +137,7 @@ func (ctx *ApiCtx) CreateDir(parentId, name string, notify bool) (*model.Documen
104137

105138
files := &archive.DocumentFiles{}
106139

107-
tmpDir, err := ioutil.TempDir("", "rmupload")
140+
tmpDir, err := os.MkdirTemp("", "rmupload")
108141
if err != nil {
109142
return nil, err
110143
}
@@ -219,10 +252,11 @@ func Sync(b *BlobStorage, tree *HashTree, operation func(t *HashTree) error) err
219252

220253
log.Info.Println("wrong generation, re-reading remote tree")
221254
//resync and try again
222-
err = tree.Mirror(b)
255+
err = tree.Mirror(b, concurrent)
223256
if err != nil {
224257
return err
225258
}
259+
log.Warning.Println("remote tree has changed, refresh the file tree")
226260
}
227261
return saveTree(tree)
228262
}
@@ -258,10 +292,10 @@ func (ctx *ApiCtx) MoveEntry(src, dstDir *model.Node, name string) (*model.Node,
258292
if err != nil {
259293
return err
260294
}
261-
doc.MetadataFile.Version += 1
262-
doc.MetadataFile.DocName = name
263-
doc.MetadataFile.Parent = dstDir.Id()
264-
doc.MetadataFile.MetadataModified = true
295+
doc.Metadata.Version += 1
296+
doc.Metadata.DocName = name
297+
doc.Metadata.Parent = dstDir.Id()
298+
doc.Metadata.MetadataModified = true
265299

266300
hashStr, reader, err := doc.MetadataHashAndReader()
267301
if err != nil {
@@ -306,7 +340,7 @@ func (ctx *ApiCtx) MoveEntry(src, dstDir *model.Node, name string) (*model.Node,
306340
return nil, err
307341
}
308342

309-
return &model.Node{d.ToDocument(), src.Children, dstDir}, nil
343+
return &model.Node{Document: d.ToDocument(), Children: src.Children, Parent: dstDir}, nil
310344
}
311345

312346
// UploadDocument uploads a local document given by sourceDocPath under the parentId directory
@@ -324,7 +358,7 @@ func (ctx *ApiCtx) UploadDocument(parentId string, sourceDocPath string, notify
324358

325359
var err error
326360

327-
tmpDir, err := ioutil.TempDir("", "rmupload")
361+
tmpDir, err := os.MkdirTemp("", "rmupload")
328362
if err != nil {
329363
return nil, err
330364
}
@@ -391,33 +425,14 @@ func (ctx *ApiCtx) UploadDocument(parentId string, sourceDocPath string, notify
391425
return doc.ToDocument(), nil
392426
}
393427

394-
func CreateCtx(http *transport.HttpClientCtx) (*ApiCtx, error) {
395-
apiStorage := &BlobStorage{http}
396-
cacheTree, err := loadTree()
397-
if err != nil {
398-
fmt.Print(err)
399-
return nil, err
400-
}
401-
err = cacheTree.Mirror(apiStorage)
402-
if err != nil {
403-
return nil, err
404-
}
405-
saveTree(cacheTree)
406-
tree, err := DocumentsFileTree(cacheTree)
407-
if err != nil {
408-
return nil, fmt.Errorf("failed to fetch document tree %v", err)
409-
}
410-
return &ApiCtx{http, tree, apiStorage, cacheTree}, nil
411-
}
412-
413428
// DocumentsFileTree reads your remote documents and builds a file tree
414429
// structure to represent them
415-
func DocumentsFileTree(tree *HashTree) (*filetree.FileTreeCtx, error) {
430+
func DocumentsFileTree(tree *HashTree) *filetree.FileTreeCtx {
416431

417432
documents := make([]*model.Document, 0)
418433
for _, d := range tree.Docs {
419434
//dont show deleted (already cached)
420-
if d.Deleted {
435+
if d.Metadata.Deleted {
421436
continue
422437
}
423438
doc := d.ToDocument()
@@ -434,7 +449,7 @@ func DocumentsFileTree(tree *HashTree) (*filetree.FileTreeCtx, error) {
434449
log.Trace.Println(d.Name(), d.IsFile())
435450
}
436451

437-
return &fileTree, nil
452+
return &fileTree
438453
}
439454

440455
// SyncComplete notfies that somethings has changed (triggers tablet sync)

api/sync15/blobdoc.go

+12-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"encoding/json"
99
"errors"
1010
"io"
11-
"io/ioutil"
1211
"sort"
1312
"strconv"
1413
"strings"
@@ -22,12 +21,12 @@ import (
2221
type BlobDoc struct {
2322
Files []*Entry
2423
Entry
25-
archive.MetadataFile
24+
Metadata archive.MetadataFile
2625
}
2726

2827
func NewBlobDoc(name, documentId, colType, parentId string) *BlobDoc {
2928
return &BlobDoc{
30-
MetadataFile: archive.MetadataFile{
29+
Metadata: archive.MetadataFile{
3130
DocName: name,
3231
CollectionType: colType,
3332
LastModified: archive.UnixTimestamp(),
@@ -52,7 +51,7 @@ func (d *BlobDoc) Rehash() error {
5251
}
5352

5453
func (d *BlobDoc) MetadataHashAndReader() (hash string, reader io.Reader, err error) {
55-
jsn, err := json.Marshal(d.MetadataFile)
54+
jsn, err := json.Marshal(d.Metadata)
5655
if err != nil {
5756
return
5857
}
@@ -121,7 +120,7 @@ func (d *BlobDoc) ReadMetadata(fileEntry *Entry, r RemoteStorage) error {
121120
return err
122121
}
123122
defer meta.Close()
124-
content, err := ioutil.ReadAll(meta)
123+
content, err := io.ReadAll(meta)
125124
if err != nil {
126125
return err
127126
}
@@ -130,7 +129,7 @@ func (d *BlobDoc) ReadMetadata(fileEntry *Entry, r RemoteStorage) error {
130129
log.Error.Printf("cannot read metadata %s %v", fileEntry.DocumentID, err)
131130
}
132131
log.Trace.Println("name from metadata: ", metadata.DocName)
133-
d.MetadataFile = metadata
132+
d.Metadata = metadata
134133
}
135134

136135
return nil
@@ -208,10 +207,10 @@ func (d *BlobDoc) Mirror(e *Entry, r RemoteStorage) error {
208207
}
209208
func (d *BlobDoc) ToDocument() *model.Document {
210209
var lastModified string
211-
unixTime, err := strconv.ParseInt(d.MetadataFile.LastModified, 10, 64)
210+
unixTime, err := strconv.ParseInt(d.Metadata.LastModified, 10, 64)
212211
if err == nil {
213212
//HACK: convert wrong nano timestamps to millis
214-
if len(d.MetadataFile.LastModified) > 18 {
213+
if len(d.Metadata.LastModified) > 18 {
215214
unixTime /= 1000000
216215
}
217216

@@ -220,11 +219,11 @@ func (d *BlobDoc) ToDocument() *model.Document {
220219
}
221220
return &model.Document{
222221
ID: d.DocumentID,
223-
VissibleName: d.MetadataFile.DocName,
224-
Version: d.MetadataFile.Version,
225-
Parent: d.MetadataFile.Parent,
226-
Type: d.MetadataFile.CollectionType,
227-
CurrentPage: d.MetadataFile.LastOpenedPage,
222+
VissibleName: d.Metadata.DocName,
223+
Version: d.Metadata.Version,
224+
Parent: d.Metadata.Parent,
225+
Type: d.Metadata.CollectionType,
226+
CurrentPage: d.Metadata.LastOpenedPage,
228227
ModifiedClient: lastModified,
229228
}
230229
}

api/sync15/blobstorage.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package sync15
33
import (
44
"bytes"
55
"io"
6-
"io/ioutil"
76
"net/http"
87

98
"github.com/juruen/rmapi/config"
@@ -13,7 +12,14 @@ import (
1312
)
1413

1514
type BlobStorage struct {
16-
http *transport.HttpClientCtx
15+
http *transport.HttpClientCtx
16+
concurrency int
17+
}
18+
19+
func NewBlobStorage(http *transport.HttpClientCtx) *BlobStorage {
20+
return &BlobStorage{
21+
http: http,
22+
}
1723
}
1824

1925
const ROOT_NAME = "root"
@@ -111,7 +117,7 @@ func (b *BlobStorage) GetRootIndex() (string, int64, error) {
111117
if err != nil {
112118
return "", 0, err
113119
}
114-
content, err := ioutil.ReadAll(blob)
120+
content, err := io.ReadAll(blob)
115121
if err != nil {
116122
return "", 0, err
117123
}

api/sync15/common.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"crypto/sha256"
55
"encoding/hex"
66
"encoding/json"
7-
"io/ioutil"
87
"os"
98
"path"
109
"sort"
@@ -42,14 +41,16 @@ func getCachedTreePath() (string, error) {
4241
return cacheFile, nil
4342
}
4443

44+
const cacheVersion = 2
45+
4546
func loadTree() (*HashTree, error) {
46-
tree := &HashTree{}
4747
cacheFile, err := getCachedTreePath()
4848
if err != nil {
4949
return nil, err
5050
}
51+
tree := &HashTree{}
5152
if _, err := os.Stat(cacheFile); err == nil {
52-
b, err := ioutil.ReadFile(cacheFile)
53+
b, err := os.ReadFile(cacheFile)
5354
if err != nil {
5455
return nil, err
5556
}
@@ -58,8 +59,10 @@ func loadTree() (*HashTree, error) {
5859
log.Error.Println("cache corrupt")
5960
return tree, nil
6061
}
61-
} else {
62-
os.Create(cacheFile)
62+
if tree.CacheVersion != cacheVersion {
63+
log.Info.Println("wrong cache file version, resync")
64+
return &HashTree{}, nil
65+
}
6366
}
6467
log.Info.Println("cache loaded: ", cacheFile)
6568

@@ -72,10 +75,11 @@ func saveTree(tree *HashTree) error {
7275
if err != nil {
7376
return err
7477
}
78+
tree.CacheVersion = cacheVersion
7579
b, err := json.MarshalIndent(tree, "", "")
7680
if err != nil {
7781
return err
7882
}
79-
err = ioutil.WriteFile(cacheFile, b, 0644)
83+
err = os.WriteFile(cacheFile, b, 0644)
8084
return err
8185
}

0 commit comments

Comments
 (0)