-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reply to "wants" messages with "has" messages #12
Changes from 6 commits
363be6f
7a98547
039913b
a3cf15c
163860d
593a870
be29d6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package replication | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the code in this file was moved here from other files. |
||
|
||
import ( | ||
"context" | ||
"io" | ||
|
||
"github.com/boreq/errors" | ||
"github.com/planetary-social/go-ssb/service/domain/blobs" | ||
"github.com/planetary-social/go-ssb/service/domain/refs" | ||
"github.com/planetary-social/go-ssb/service/domain/transport" | ||
) | ||
|
||
type WantListStorage interface { | ||
GetWantList() (blobs.WantList, error) | ||
} | ||
|
||
type Downloader interface { | ||
OnHasReceived(ctx context.Context, peer transport.Peer, blob refs.Blob, size blobs.Size) | ||
} | ||
|
||
var ErrBlobNotFound = errors.New("blob not found") | ||
|
||
type BlobStorage interface { | ||
Store(id refs.Blob, r io.Reader) error | ||
|
||
// Size returns the size of the blob. If the blob is not found it returns | ||
// ErrBlobNotFound. | ||
Size(id refs.Blob) (blobs.Size, error) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,118 +2,78 @@ package replication | |
|
||
import ( | ||
"context" | ||
"time" | ||
"sync" | ||
|
||
"github.com/boreq/errors" | ||
"github.com/planetary-social/go-ssb/logging" | ||
"github.com/planetary-social/go-ssb/service/domain/blobs" | ||
"github.com/planetary-social/go-ssb/service/domain/messages" | ||
"github.com/planetary-social/go-ssb/service/domain/refs" | ||
"github.com/planetary-social/go-ssb/service/domain/transport" | ||
"github.com/planetary-social/go-ssb/service/domain/transport/rpc" | ||
) | ||
|
||
type WantListStorage interface { | ||
GetWantList() (blobs.WantList, error) | ||
} | ||
|
||
type Downloader interface { | ||
OnHasReceived(ctx context.Context, peer transport.Peer, blob refs.Blob, size blobs.Size) | ||
} | ||
|
||
type Manager struct { | ||
storage WantListStorage | ||
downloader Downloader | ||
logger logging.Logger | ||
wantListStorage WantListStorage | ||
blobStorage BlobSizeRepository | ||
downloader Downloader | ||
logger logging.Logger | ||
|
||
// todo cleanup processes | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be done later, it could even stay like this all the way to production unless we ran the app for a very long time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I understand the underlying process goroutines for incoming and outgoing loops will get terminated on connection context termination anyway, right? So the only thing we'd have to improve would be reacting to connection termination by deleting the process struct from this map (for example by running a goroutine awaiting ctx close while creating a new process in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. |
||
processes map[rpc.ConnectionId]*WantsProcess | ||
lock sync.Mutex // guards processes | ||
} | ||
|
||
func NewManager(storage WantListStorage, downloader Downloader, logger logging.Logger) *Manager { | ||
func NewManager( | ||
wantListStorage WantListStorage, | ||
blobStorage BlobSizeRepository, | ||
downloader Downloader, | ||
logger logging.Logger, | ||
) *Manager { | ||
return &Manager{ | ||
storage: storage, | ||
downloader: downloader, | ||
logger: logger.New("replication_manager"), | ||
wantListStorage: wantListStorage, | ||
blobStorage: blobStorage, | ||
downloader: downloader, | ||
processes: make(map[rpc.ConnectionId]*WantsProcess), | ||
logger: logger.New("replication_manager"), | ||
} | ||
} | ||
|
||
func (r *Manager) HandleIncomingCreateWantsRequest(ctx context.Context) (<-chan messages.BlobWithSizeOrWantDistance, error) { | ||
func (m *Manager) HandleIncomingCreateWantsRequest(ctx context.Context) (<-chan messages.BlobWithSizeOrWantDistance, error) { | ||
connectionId, ok := rpc.GetConnectionIdFromContext(ctx) | ||
if !ok { | ||
return nil, errors.New("connection id not found in context") | ||
} | ||
r.logger.WithField("connectionId", connectionId).Debug("incoming create wants") | ||
|
||
m.lock.Lock() | ||
defer m.lock.Unlock() | ||
|
||
ch := make(chan messages.BlobWithSizeOrWantDistance) | ||
go r.sendWantListPeriodically(ctx, ch) | ||
m.getOrCreateProcess(connectionId).AddIncoming(ctx, ch) | ||
return ch, nil | ||
} | ||
|
||
func (r *Manager) HandleOutgoingCreateWantsRequest(ctx context.Context, ch <-chan messages.BlobWithSizeOrWantDistance, peer transport.Peer) error { | ||
func (m *Manager) HandleOutgoingCreateWantsRequest(ctx context.Context, ch <-chan messages.BlobWithSizeOrWantDistance, peer transport.Peer) error { | ||
connectionId, ok := rpc.GetConnectionIdFromContext(ctx) | ||
if !ok { | ||
return errors.New("connection id not found in context") | ||
} | ||
r.logger.WithField("connectionId", connectionId).Debug("outgoing create wants") | ||
|
||
go r.handleOutgoing(ctx, connectionId, ch, peer) | ||
return nil | ||
} | ||
|
||
func (r *Manager) handleOutgoing(ctx context.Context, id rpc.ConnectionId, ch <-chan messages.BlobWithSizeOrWantDistance, peer transport.Peer) { | ||
for blobWithSizeOrWantDistance := range ch { | ||
logger := r.logger.WithField("connection_id", id).WithField("blob", blobWithSizeOrWantDistance.Id().String()) | ||
|
||
if size, ok := blobWithSizeOrWantDistance.SizeOrWantDistance().Size(); ok { | ||
logger.WithField("size", size.InBytes()).Debug("got size") | ||
go r.downloader.OnHasReceived(ctx, peer, blobWithSizeOrWantDistance.Id(), size) | ||
continue | ||
} | ||
|
||
if distance, ok := blobWithSizeOrWantDistance.SizeOrWantDistance().WantDistance(); ok { | ||
// peer wants a blob | ||
// todo tell it that we have it if we have it | ||
logger.WithField("distance", distance.Int()).Debug("got distance") | ||
continue | ||
} | ||
m.lock.Lock() | ||
defer m.lock.Unlock() | ||
|
||
panic("logic error") | ||
} | ||
|
||
// todo channel closed | ||
m.getOrCreateProcess(connectionId).AddOutgoing(ctx, ch, peer) | ||
return nil | ||
} | ||
|
||
func (r *Manager) sendWantListPeriodically(ctx context.Context, ch chan<- messages.BlobWithSizeOrWantDistance) { | ||
defer close(ch) | ||
defer r.logger.Debug("terminating sending want list") | ||
|
||
for { | ||
wl, err := r.storage.GetWantList() | ||
if err != nil { | ||
r.logger.WithError(err).Error("could not get the want list") | ||
continue | ||
} | ||
|
||
for _, v := range wl.List() { | ||
v, err := messages.NewBlobWithWantDistance(v.Id, v.Distance) | ||
if err != nil { | ||
r.logger.WithError(err).Error("could not create a blob with want distance") | ||
continue | ||
} | ||
|
||
r.logger.WithField("blob", v.Id()).Debug("sending wants") | ||
|
||
select { | ||
case ch <- v: | ||
continue | ||
case <-ctx.Done(): | ||
return | ||
} | ||
} | ||
|
||
select { | ||
case <-ctx.Done(): | ||
return | ||
case <-time.After(10 * time.Second): // todo change | ||
continue | ||
} | ||
func (m *Manager) getOrCreateProcess(id rpc.ConnectionId) *WantsProcess { | ||
v, ok := m.processes[id] | ||
if !ok { | ||
v = NewWantsProcess( | ||
m.wantListStorage, | ||
m.blobStorage, | ||
m.downloader, | ||
m.logger.WithField("connection_id", id), | ||
) | ||
m.processes[id] = v | ||
} | ||
return v | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not important, this whole file is just a test program not used in production.