Rework storage.TorrentImpl to support shared capacity key
This commit is contained in:
parent
6e97ce952f
commit
5f8471e21b
|
@ -15,11 +15,9 @@ type badStorage struct{}
|
||||||
var _ storage.ClientImpl = badStorage{}
|
var _ storage.ClientImpl = badStorage{}
|
||||||
|
|
||||||
func (bs badStorage) OpenTorrent(*metainfo.Info, metainfo.Hash) (storage.TorrentImpl, error) {
|
func (bs badStorage) OpenTorrent(*metainfo.Info, metainfo.Hash) (storage.TorrentImpl, error) {
|
||||||
return bs, nil
|
return storage.TorrentImpl{
|
||||||
}
|
Piece: bs.Piece,
|
||||||
|
}, nil
|
||||||
func (bs badStorage) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs badStorage) Piece(p metainfo.Piece) storage.PieceImpl {
|
func (bs badStorage) Piece(p metainfo.Piece) storage.PieceImpl {
|
||||||
|
|
|
@ -98,7 +98,7 @@ func BenchmarkConnectionMainReadLoop(b *testing.B) {
|
||||||
ts := &torrentStorage{}
|
ts := &torrentStorage{}
|
||||||
t := &Torrent{
|
t := &Torrent{
|
||||||
cl: cl,
|
cl: cl,
|
||||||
storage: &storage.Torrent{TorrentImpl: ts},
|
storage: &storage.Torrent{TorrentImpl: storage.TorrentImpl{Piece: ts.Piece, Close: ts.Close}},
|
||||||
pieceStateChanges: pubsub.NewPubSub(),
|
pieceStateChanges: pubsub.NewPubSub(),
|
||||||
}
|
}
|
||||||
require.NoError(b, t.setInfo(&metainfo.Info{
|
require.NoError(b, t.setInfo(&metainfo.Info{
|
||||||
|
|
|
@ -43,7 +43,11 @@ func (me *boltClient) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me *boltClient) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
|
func (me *boltClient) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
|
||||||
return &boltTorrent{me, infoHash}, nil
|
t := &boltTorrent{me, infoHash}
|
||||||
|
return TorrentImpl{
|
||||||
|
Piece: t.Piece,
|
||||||
|
Close: t.Close,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me *boltTorrent) Piece(p metainfo.Piece) PieceImpl {
|
func (me *boltTorrent) Piece(p metainfo.Piece) PieceImpl {
|
||||||
|
|
|
@ -67,14 +67,16 @@ func (me *fileClientImpl) Close() error {
|
||||||
return me.pc.Close()
|
return me.pc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
|
func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (_ TorrentImpl, err error) {
|
||||||
dir := fs.pathMaker(fs.baseDir, info, infoHash)
|
dir := fs.pathMaker(fs.baseDir, info, infoHash)
|
||||||
upvertedFiles := info.UpvertedFiles()
|
upvertedFiles := info.UpvertedFiles()
|
||||||
files := make([]file, 0, len(upvertedFiles))
|
files := make([]file, 0, len(upvertedFiles))
|
||||||
for i, fileInfo := range upvertedFiles {
|
for i, fileInfo := range upvertedFiles {
|
||||||
s, err := ToSafeFilePath(append([]string{info.Name}, fileInfo.Path...)...)
|
var s string
|
||||||
|
s, err = ToSafeFilePath(append([]string{info.Name}, fileInfo.Path...)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("file %v has unsafe path %q: %w", i, fileInfo.Path, err)
|
err = fmt.Errorf("file %v has unsafe path %q: %w", i, fileInfo.Path, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
f := file{
|
f := file{
|
||||||
path: filepath.Join(dir, s),
|
path: filepath.Join(dir, s),
|
||||||
|
@ -83,16 +85,21 @@ func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Has
|
||||||
if f.length == 0 {
|
if f.length == 0 {
|
||||||
err = CreateNativeZeroLengthFile(f.path)
|
err = CreateNativeZeroLengthFile(f.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("creating zero length file: %w", err)
|
err = fmt.Errorf("creating zero length file: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
return &fileTorrentImpl{
|
t := &fileTorrentImpl{
|
||||||
files,
|
files,
|
||||||
segments.NewIndex(common.LengthIterFromUpvertedFiles(upvertedFiles)),
|
segments.NewIndex(common.LengthIterFromUpvertedFiles(upvertedFiles)),
|
||||||
infoHash,
|
infoHash,
|
||||||
fs.pc,
|
fs.pc,
|
||||||
|
}
|
||||||
|
return TorrentImpl{
|
||||||
|
Piece: t.Piece,
|
||||||
|
Close: t.Close,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,11 @@ type ClientImpl interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data storage bound to a torrent.
|
// Data storage bound to a torrent.
|
||||||
type TorrentImpl interface {
|
type TorrentImpl struct {
|
||||||
Piece(metainfo.Piece) PieceImpl
|
Piece func(metainfo.Piece) PieceImpl
|
||||||
Close() error
|
Close func() error
|
||||||
|
// Storages that share the same value, will provide a pointer to the same function.
|
||||||
|
Capacity *func() *int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interacts with torrent piece data. Optional interfaces to implement include io.WriterTo, such as
|
// Interacts with torrent piece data. Optional interfaces to implement include io.WriterTo, such as
|
||||||
|
|
|
@ -30,14 +30,14 @@ func NewMMapWithCompletion(baseDir string, completion PieceCompletion) *mmapClie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mmapClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (t TorrentImpl, err error) {
|
func (s *mmapClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (_ TorrentImpl, err error) {
|
||||||
span, err := mMapTorrent(info, s.baseDir)
|
span, err := mMapTorrent(info, s.baseDir)
|
||||||
t = &mmapTorrentStorage{
|
t := &mmapTorrentStorage{
|
||||||
infoHash: infoHash,
|
infoHash: infoHash,
|
||||||
span: span,
|
span: span,
|
||||||
pc: s.pc,
|
pc: s.pc,
|
||||||
}
|
}
|
||||||
return
|
return TorrentImpl{Piece: t.Piece, Close: t.Close}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *mmapClientImpl) Close() error {
|
func (s *mmapClientImpl) Close() error {
|
||||||
|
|
|
@ -26,6 +26,7 @@ type ResourcePiecesOpts struct {
|
||||||
// Sized puts require being able to stream from a statement executed on another connection.
|
// Sized puts require being able to stream from a statement executed on another connection.
|
||||||
// Without them, we buffer the entire read and then put that.
|
// Without them, we buffer the entire read and then put that.
|
||||||
NoSizedPuts bool
|
NoSizedPuts bool
|
||||||
|
Capacity *int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResourcePieces(p PieceProvider) ClientImpl {
|
func NewResourcePieces(p PieceProvider) ClientImpl {
|
||||||
|
@ -49,10 +50,11 @@ func (piecePerResourceTorrentImpl) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s piecePerResource) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
|
func (s piecePerResource) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
|
||||||
return piecePerResourceTorrentImpl{
|
t := piecePerResourceTorrentImpl{
|
||||||
s,
|
s,
|
||||||
make([]sync.RWMutex, info.NumPieces()),
|
make([]sync.RWMutex, info.NumPieces()),
|
||||||
}, nil
|
}
|
||||||
|
return TorrentImpl{Piece: t.Piece, Close: t.Close}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s piecePerResourceTorrentImpl) Piece(p metainfo.Piece) PieceImpl {
|
func (s piecePerResourceTorrentImpl) Piece(p metainfo.Piece) PieceImpl {
|
||||||
|
|
|
@ -61,9 +61,24 @@ func NewDirectStorage(opts NewDirectStorageOpts) (_ storage.ClientImplCloser, er
|
||||||
if opts.BlobFlushInterval != 0 {
|
if opts.BlobFlushInterval != 0 {
|
||||||
cl.blobFlusher = time.AfterFunc(opts.BlobFlushInterval, cl.blobFlusherFunc)
|
cl.blobFlusher = time.AfterFunc(opts.BlobFlushInterval, cl.blobFlusherFunc)
|
||||||
}
|
}
|
||||||
|
cl.capacity = cl.getCapacity
|
||||||
return cl, nil
|
return cl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cl *client) getCapacity() (ret *int64) {
|
||||||
|
cl.l.Lock()
|
||||||
|
defer cl.l.Unlock()
|
||||||
|
err := sqlitex.Exec(cl.conn, "select value from setting where name='capacity'", func(stmt *sqlite.Stmt) error {
|
||||||
|
ret = new(int64)
|
||||||
|
*ret = stmt.ColumnInt64(0)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
l sync.Mutex
|
l sync.Mutex
|
||||||
conn conn
|
conn conn
|
||||||
|
@ -71,6 +86,7 @@ type client struct {
|
||||||
blobFlusher *time.Timer
|
blobFlusher *time.Timer
|
||||||
opts NewDirectStorageOpts
|
opts NewDirectStorageOpts
|
||||||
closed bool
|
closed bool
|
||||||
|
capacity func() *int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) blobFlusherFunc() {
|
func (c *client) blobFlusherFunc() {
|
||||||
|
@ -91,7 +107,8 @@ func (c *client) flushBlobs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (storage.TorrentImpl, error) {
|
func (c *client) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (storage.TorrentImpl, error) {
|
||||||
return torrent{c}, nil
|
t := torrent{c}
|
||||||
|
return storage.TorrentImpl{Piece: t.Piece, Close: t.Close, Capacity: &c.capacity}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Close() error {
|
func (c *client) Close() error {
|
||||||
|
|
|
@ -124,7 +124,7 @@ func (me *diskFullStorage) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskFullStorage) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (storage.TorrentImpl, error) {
|
func (d *diskFullStorage) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (storage.TorrentImpl, error) {
|
||||||
return d, nil
|
return storage.TorrentImpl{Piece: d.Piece, Close: d.Close}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type pieceImpl struct {
|
type pieceImpl struct {
|
||||||
|
|
|
@ -192,7 +192,6 @@ func testClientTransfer(t *testing.T, ps testClientTransferParams) {
|
||||||
type fileCacheClientStorageFactoryParams struct {
|
type fileCacheClientStorageFactoryParams struct {
|
||||||
Capacity int64
|
Capacity int64
|
||||||
SetCapacity bool
|
SetCapacity bool
|
||||||
Wrapper func(*filecache.Cache) storage.ClientImplCloser
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFileCacheClientStorageFactory(ps fileCacheClientStorageFactoryParams) storageFactory {
|
func newFileCacheClientStorageFactory(ps fileCacheClientStorageFactoryParams) storageFactory {
|
||||||
|
@ -201,10 +200,22 @@ func newFileCacheClientStorageFactory(ps fileCacheClientStorageFactoryParams) st
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
var sharedCapacity *int64
|
||||||
if ps.SetCapacity {
|
if ps.SetCapacity {
|
||||||
|
sharedCapacity = &ps.Capacity
|
||||||
fc.SetCapacity(ps.Capacity)
|
fc.SetCapacity(ps.Capacity)
|
||||||
}
|
}
|
||||||
return ps.Wrapper(fc)
|
return struct {
|
||||||
|
storage.ClientImpl
|
||||||
|
io.Closer
|
||||||
|
}{
|
||||||
|
storage.NewResourcePiecesOpts(
|
||||||
|
fc.AsResourceProvider(),
|
||||||
|
storage.ResourcePiecesOpts{
|
||||||
|
Capacity: sharedCapacity,
|
||||||
|
}),
|
||||||
|
ioutil.NopCloser(nil),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,17 +223,13 @@ type storageFactory func(string) storage.ClientImplCloser
|
||||||
|
|
||||||
func TestClientTransferDefault(t *testing.T) {
|
func TestClientTransferDefault(t *testing.T) {
|
||||||
testClientTransfer(t, testClientTransferParams{
|
testClientTransfer(t, testClientTransferParams{
|
||||||
LeecherStorage: newFileCacheClientStorageFactory(fileCacheClientStorageFactoryParams{
|
LeecherStorage: newFileCacheClientStorageFactory(fileCacheClientStorageFactoryParams{}),
|
||||||
Wrapper: fileCachePieceResourceStorage,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientTransferDefaultNoMetadata(t *testing.T) {
|
func TestClientTransferDefaultNoMetadata(t *testing.T) {
|
||||||
testClientTransfer(t, testClientTransferParams{
|
testClientTransfer(t, testClientTransferParams{
|
||||||
LeecherStorage: newFileCacheClientStorageFactory(fileCacheClientStorageFactoryParams{
|
LeecherStorage: newFileCacheClientStorageFactory(fileCacheClientStorageFactoryParams{}),
|
||||||
Wrapper: fileCachePieceResourceStorage,
|
|
||||||
}),
|
|
||||||
LeecherStartsWithoutMetadata: true,
|
LeecherStartsWithoutMetadata: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -244,16 +251,6 @@ func TestClientTransferRateLimitedDownload(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImplCloser {
|
|
||||||
return struct {
|
|
||||||
storage.ClientImpl
|
|
||||||
io.Closer
|
|
||||||
}{
|
|
||||||
storage.NewResourcePieces(fc.AsResourceProvider()),
|
|
||||||
ioutil.NopCloser(nil),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testClientTransferSmallCache(t *testing.T, setReadahead bool, readahead int64) {
|
func testClientTransferSmallCache(t *testing.T, setReadahead bool, readahead int64) {
|
||||||
testClientTransfer(t, testClientTransferParams{
|
testClientTransfer(t, testClientTransferParams{
|
||||||
LeecherStorage: newFileCacheClientStorageFactory(fileCacheClientStorageFactoryParams{
|
LeecherStorage: newFileCacheClientStorageFactory(fileCacheClientStorageFactoryParams{
|
||||||
|
@ -261,7 +258,6 @@ func testClientTransferSmallCache(t *testing.T, setReadahead bool, readahead int
|
||||||
// Going below the piece length means it can't complete a piece so
|
// Going below the piece length means it can't complete a piece so
|
||||||
// that it can be hashed.
|
// that it can be hashed.
|
||||||
Capacity: 5,
|
Capacity: 5,
|
||||||
Wrapper: fileCachePieceResourceStorage,
|
|
||||||
}),
|
}),
|
||||||
SetReadahead: setReadahead,
|
SetReadahead: setReadahead,
|
||||||
// Can't readahead too far or the cache will thrash and drop data we
|
// Can't readahead too far or the cache will thrash and drop data we
|
||||||
|
@ -324,9 +320,7 @@ func sqliteLeecherStorageTestCase(numConns int) leecherStorageTestCase {
|
||||||
func TestClientTransferVarious(t *testing.T) {
|
func TestClientTransferVarious(t *testing.T) {
|
||||||
// Leecher storage
|
// Leecher storage
|
||||||
for _, ls := range []leecherStorageTestCase{
|
for _, ls := range []leecherStorageTestCase{
|
||||||
{"Filecache", newFileCacheClientStorageFactory(fileCacheClientStorageFactoryParams{
|
{"Filecache", newFileCacheClientStorageFactory(fileCacheClientStorageFactoryParams{}), 0},
|
||||||
Wrapper: fileCachePieceResourceStorage,
|
|
||||||
}), 0},
|
|
||||||
{"Boltdb", storage.NewBoltDB, 0},
|
{"Boltdb", storage.NewBoltDB, 0},
|
||||||
{"SqliteDirect", func(s string) storage.ClientImplCloser {
|
{"SqliteDirect", func(s string) storage.ClientImplCloser {
|
||||||
path := filepath.Join(s, "sqlite3.db")
|
path := filepath.Join(s, "sqlite3.db")
|
||||||
|
|
|
@ -760,7 +760,9 @@ func (t *Torrent) close() (err error) {
|
||||||
func() {
|
func() {
|
||||||
t.storageLock.Lock()
|
t.storageLock.Lock()
|
||||||
defer t.storageLock.Unlock()
|
defer t.storageLock.Unlock()
|
||||||
t.storage.Close()
|
if f := t.storage.Close; f != nil {
|
||||||
|
f()
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
t.iterPeers(func(p *Peer) {
|
t.iterPeers(func(p *Peer) {
|
||||||
|
|
Loading…
Reference in New Issue