taildrop: implement asynchronous file deletion (#9844)

File resumption requires keeping partial files around for some time,
but we must still eventually delete them if never resumed.
Thus, we implement asynchronous file deletion, which could
spawn a background goroutine to delete the files.

We also use the same mechanism for deleting files on Windows,
where a file can't be deleted if there is still an open file handle.
We can enqueue those with the asynchronous file deleter as well.

Updates tailscale/corp#14772

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
This commit is contained in:
Joe Tsai
2023-10-17 13:46:05 -07:00
committed by GitHub
parent 33bb2bbfe9
commit c2a551469c
10 changed files with 560 additions and 366 deletions

View File

@@ -646,6 +646,9 @@ func (b *LocalBackend) Shutdown() {
if b.sockstatLogger != nil {
b.sockstatLogger.Shutdown()
}
if b.peerAPIServer != nil {
b.peerAPIServer.taildrop.Shutdown()
}
b.unregisterNetMon()
b.unregisterHealthWatch()
@@ -3614,14 +3617,14 @@ func (b *LocalBackend) initPeerAPIListener() {
ps := &peerAPIServer{
b: b,
taildrop: &taildrop.Manager{
taildrop: taildrop.ManagerOptions{
Logf: b.logf,
Clock: tstime.DefaultClock{b.clock},
Clock: tstime.DefaultClock{Clock: b.clock},
Dir: fileRoot,
DirectFileMode: b.directFileRoot != "",
AvoidFinalRename: !b.directFileDoFinalRename,
SendFileNotify: b.sendFileNotify,
},
}.New(),
}
if dm, ok := b.sys.DNSManager.GetOK(); ok {
ps.resolver = dm.Resolver()

View File

@@ -68,7 +68,7 @@ func bodyNotContains(sub string) check {
func fileHasSize(name string, size int) check {
return func(t *testing.T, e *peerAPITestEnv) {
root := e.ph.ps.taildrop.Dir
root := e.ph.ps.taildrop.Dir()
if root == "" {
t.Errorf("no rootdir; can't check whether %q has size %v", name, size)
return
@@ -84,7 +84,7 @@ func fileHasSize(name string, size int) check {
func fileHasContents(name string, want string) check {
return func(t *testing.T, e *peerAPITestEnv) {
root := e.ph.ps.taildrop.Dir
root := e.ph.ps.taildrop.Dir()
if root == "" {
t.Errorf("no rootdir; can't check contents of %q", name)
return
@@ -540,11 +540,11 @@ func TestHandlePeerAPI(t *testing.T) {
if !tt.omitRoot {
rootDir = t.TempDir()
if e.ph.ps.taildrop == nil {
e.ph.ps.taildrop = &taildrop.Manager{
e.ph.ps.taildrop = taildrop.ManagerOptions{
Logf: e.logBuf.Logf,
}
Dir: rootDir,
}.New()
}
e.ph.ps.taildrop.Dir = rootDir
}
for _, req := range tt.reqs {
e.rr = httptest.NewRecorder()
@@ -583,10 +583,10 @@ func TestFileDeleteRace(t *testing.T) {
capFileSharing: true,
clock: &tstest.Clock{},
},
taildrop: &taildrop.Manager{
taildrop: taildrop.ManagerOptions{
Logf: t.Logf,
Dir: dir,
},
}.New(),
}
ph := &peerAPIHandler{
isSelf: true,