mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-08 22:31:04 +00:00
Improve puller somewhat
This commit is contained in:
parent
c70fef1208
commit
704e0fa6b8
28
main.go
28
main.go
@ -21,15 +21,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
ConfDir string `short:"c" long:"cfg" description:"Configuration directory" default:"~/.syncthing" value-name:"DIR"`
|
ConfDir string `short:"c" long:"cfg" description:"Configuration directory" default:"~/.syncthing" value-name:"DIR"`
|
||||||
Listen string `short:"l" long:"listen" description:"Listen address" default:":22000" value-name:"ADDR"`
|
Listen string `short:"l" long:"listen" description:"Listen address" default:":22000" value-name:"ADDR"`
|
||||||
ReadOnly bool `short:"r" long:"ro" description:"Repository is read only"`
|
ReadOnly bool `short:"r" long:"ro" description:"Repository is read only"`
|
||||||
Delete bool `short:"d" long:"delete" description:"Delete files from repo when deleted from cluster"`
|
Delete bool `short:"d" long:"delete" description:"Delete files deleted from cluster"`
|
||||||
NoSymlinks bool `long:"no-symlinks" description:"Don't follow first level symlinks in the repo"`
|
NoSymlinks bool `long:"no-symlinks" description:"Don't follow first level symlinks in the repo"`
|
||||||
ScanInterval time.Duration `long:"scan-intv" description:"Repository scan interval" default:"60s" value-name:"INTV"`
|
Discovery DiscoveryOptions `group:"Discovery Options"`
|
||||||
ConnInterval time.Duration `long:"conn-intv" description:"Node reconnect interval" default:"60s" value-name:"INTV"`
|
Advanced AdvancedOptions `group:"Advanced Options"`
|
||||||
Discovery DiscoveryOptions `group:"Discovery Options"`
|
Debug DebugOptions `group:"Debugging Options"`
|
||||||
Debug DebugOptions `group:"Debugging Options"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DebugOptions struct {
|
type DebugOptions struct {
|
||||||
@ -46,6 +45,13 @@ type DiscoveryOptions struct {
|
|||||||
NoLocalDiscovery bool `short:"N" long:"no-local-announce" description:"Do not announce presence locally"`
|
NoLocalDiscovery bool `short:"N" long:"no-local-announce" description:"Do not announce presence locally"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AdvancedOptions struct {
|
||||||
|
RequestsInFlight int `long:"reqs-in-flight" description:"Parallell in flight requests per file" default:"8" value-name:"REQS"`
|
||||||
|
FilesInFlight int `long:"files-in-flight" description:"Parallell in flight file pulls" default:"4" value-name:"FILES"`
|
||||||
|
ScanInterval time.Duration `long:"scan-intv" description:"Repository scan interval" default:"60s" value-name:"INTV"`
|
||||||
|
ConnInterval time.Duration `long:"conn-intv" description:"Node reconnect interval" default:"60s" value-name:"INTV"`
|
||||||
|
}
|
||||||
|
|
||||||
var opts Options
|
var opts Options
|
||||||
var Version string
|
var Version string
|
||||||
|
|
||||||
@ -162,7 +168,7 @@ func main() {
|
|||||||
// XXX: Should use some fsnotify mechanism.
|
// XXX: Should use some fsnotify mechanism.
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(opts.ScanInterval)
|
time.Sleep(opts.Advanced.ScanInterval)
|
||||||
updateLocalModel(m)
|
updateLocalModel(m)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -286,7 +292,7 @@ func connect(myID string, addr string, nodeAddrs map[string][]string, m *Model,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(opts.ConnInterval)
|
time.Sleep(opts.Advanced.ConnInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
147
model_puller.go
147
model_puller.go
@ -6,10 +6,10 @@ Locking
|
|||||||
=======
|
=======
|
||||||
|
|
||||||
These methods are never called from the outside so don't follow the locking
|
These methods are never called from the outside so don't follow the locking
|
||||||
policy in model.go. Instead, appropriate locks are acquired when needed and
|
policy in model.go.
|
||||||
held for as short a time as possible.
|
|
||||||
|
|
||||||
TODO(jb): Refactor this into smaller and cleaner pieces.
|
TODO(jb): Refactor this into smaller and cleaner pieces.
|
||||||
|
TODO(jb): Increase performance by taking apparent peer bandwidth into account.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -25,8 +25,6 @@ import (
|
|||||||
"github.com/calmh/syncthing/buffers"
|
"github.com/calmh/syncthing/buffers"
|
||||||
)
|
)
|
||||||
|
|
||||||
const RemoteFetchers = 8
|
|
||||||
|
|
||||||
func (m *Model) pullFile(name string) error {
|
func (m *Model) pullFile(name string) error {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
var localFile = m.local[name]
|
var localFile = m.local[name]
|
||||||
@ -81,58 +79,38 @@ func (m *Model) pullFile(name string) error {
|
|||||||
m.RLock()
|
m.RLock()
|
||||||
var nodeIDs = m.whoHas(name)
|
var nodeIDs = m.whoHas(name)
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
var remoteBlocksChan = make(chan Block)
|
var remoteBlocks = blockIterator{blocks: remote}
|
||||||
go func() {
|
for i := 0; i < opts.Advanced.RequestsInFlight; i++ {
|
||||||
for _, block := range remote {
|
curNode := nodeIDs[i%len(nodeIDs)]
|
||||||
remoteBlocksChan <- block
|
fetchDone.Add(1)
|
||||||
}
|
|
||||||
close(remoteBlocksChan)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// XXX: This should be rewritten into something nicer that takes differing
|
go func(nodeID string) {
|
||||||
// peer performance into account.
|
for {
|
||||||
|
block, ok := remoteBlocks.Next()
|
||||||
for i := 0; i < RemoteFetchers; i++ {
|
if !ok {
|
||||||
for _, nodeID := range nodeIDs {
|
break
|
||||||
fetchDone.Add(1)
|
|
||||||
go func(nodeID string) {
|
|
||||||
for block := range remoteBlocksChan {
|
|
||||||
data, err := m.RequestGlobal(nodeID, name, block.Offset, block.Length, block.Hash)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
contentChan <- content{
|
|
||||||
offset: int64(block.Offset),
|
|
||||||
data: data,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fetchDone.Done()
|
data, err := m.RequestGlobal(nodeID, name, block.Offset, block.Length, block.Hash)
|
||||||
}(nodeID)
|
if err != nil {
|
||||||
}
|
break
|
||||||
|
}
|
||||||
|
contentChan <- content{
|
||||||
|
offset: int64(block.Offset),
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchDone.Done()
|
||||||
|
}(curNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchDone.Wait()
|
fetchDone.Wait()
|
||||||
close(contentChan)
|
close(contentChan)
|
||||||
applyDone.Wait()
|
applyDone.Wait()
|
||||||
|
|
||||||
rf, err := os.Open(tmpFilename)
|
err = hashCheck(tmpFilename, globalFile.Blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer rf.Close()
|
|
||||||
|
|
||||||
writtenBlocks, err := Blocks(rf, BlockSize)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(writtenBlocks) != len(globalFile.Blocks) {
|
|
||||||
return fmt.Errorf("%s: incorrect number of blocks after sync", tmpFilename)
|
|
||||||
}
|
|
||||||
for i := range writtenBlocks {
|
|
||||||
if bytes.Compare(writtenBlocks[i].Hash, globalFile.Blocks[i].Hash) != 0 {
|
|
||||||
return fmt.Errorf("%s: hash mismatch after sync\n %v\n %v", tmpFilename, writtenBlocks[i], globalFile.Blocks[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.Chtimes(tmpFilename, time.Unix(globalFile.Modified, 0), time.Unix(globalFile.Modified, 0))
|
err = os.Chtimes(tmpFilename, time.Unix(globalFile.Modified, 0), time.Unix(globalFile.Modified, 0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -148,23 +126,29 @@ func (m *Model) pullFile(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) puller() {
|
func (m *Model) puller() {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
for {
|
time.Sleep(time.Second)
|
||||||
var n string
|
|
||||||
var f File
|
|
||||||
|
|
||||||
m.RLock()
|
var ns []string
|
||||||
for n = range m.need {
|
m.RLock()
|
||||||
break // just pick first name
|
for n := range m.need {
|
||||||
}
|
ns = append(ns, n)
|
||||||
if len(n) != 0 {
|
}
|
||||||
f = m.global[n]
|
m.RUnlock()
|
||||||
}
|
|
||||||
m.RUnlock()
|
|
||||||
|
|
||||||
if len(n) == 0 {
|
if len(ns) == 0 {
|
||||||
// we got nothing
|
continue
|
||||||
break
|
}
|
||||||
|
|
||||||
|
var limiter = make(chan bool, opts.Advanced.FilesInFlight)
|
||||||
|
|
||||||
|
for _, n := range ns {
|
||||||
|
limiter <- true
|
||||||
|
|
||||||
|
f, ok := m.GlobalFile(n)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -185,8 +169,9 @@ func (m *Model) puller() {
|
|||||||
} else {
|
} else {
|
||||||
warnln(err)
|
warnln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<-limiter
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,3 +193,45 @@ func applyContent(cc <-chan content, dst io.WriterAt) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hashCheck(name string, correct []Block) error {
|
||||||
|
rf, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rf.Close()
|
||||||
|
|
||||||
|
current, err := Blocks(rf, BlockSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(current) != len(correct) {
|
||||||
|
return fmt.Errorf("%s: incorrect number of blocks after sync", name)
|
||||||
|
}
|
||||||
|
for i := range current {
|
||||||
|
if bytes.Compare(current[i].Hash, correct[i].Hash) != 0 {
|
||||||
|
return fmt.Errorf("%s: hash mismatch after sync\n %v\n %v", name, current[i], correct[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type blockIterator struct {
|
||||||
|
sync.Mutex
|
||||||
|
blocks []Block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *blockIterator) Next() (b Block, ok bool) {
|
||||||
|
i.Lock()
|
||||||
|
defer i.Unlock()
|
||||||
|
|
||||||
|
if len(i.blocks) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, i.blocks = i.blocks[0], i.blocks[1:]
|
||||||
|
ok = true
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user