Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion GVFS/GVFS.Common/Database/SqliteDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static bool HasIssue(string databasePath, PhysicalFileSystem filesystem,

try
{
string sqliteConnectionString = CreateConnectionString(databasePath);
string sqliteConnectionString = $"data source={databasePath};Pooling=False";
using (SqliteConnection integrityConnection = new SqliteConnection(sqliteConnectionString))
{
integrityConnection.Open();
Expand Down
15 changes: 15 additions & 0 deletions GVFS/GVFS.Common/Database/SqliteErrorCodes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace GVFS.Common.Database
{
/// <summary>
/// SQLite result codes used for error classification.
/// See https://www.sqlite.org/rescode.html
/// </summary>
public static class SqliteErrorCodes
{
/// <summary>SQLITE_CORRUPT (11) — database disk image is malformed</summary>
public const int Corrupt = 11;

/// <summary>SQLITE_NOTADB (26) — file is not a database</summary>
public const int NotADatabase = 26;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public void SecondCloneDoesNotDownloadAdditionalObjects()
}

[TestCase]
[SkipInCI("Product bug: repair does not fully restore corrupt BlobSizes.sql — mount crashes after repair")]
public void RepairFixesCorruptBlobSizesDatabase()
{
GVFSFunctionalTestEnlistment enlistment = this.CloneAndMountEnlistment();
Expand All @@ -74,9 +73,12 @@ public void RepairFixesCorruptBlobSizesDatabase()
blobSizesDbPath.ShouldBeAFile(this.fileSystem);
this.fileSystem.WriteAllText(blobSizesDbPath, "0000");

// GVFS now tolerates corrupt blob sizes DB on mount (recreates
// in-memory), but repair should still fix the underlying file.
// Repair should detect and fix the corrupt database
enlistment.Repair(confirm: true);

// Verify repair actually cleaned up the corrupt file
blobSizesRoot.ShouldNotExistOnDisk(this.fileSystem);

enlistment.MountGVFS();
}

Expand Down
79 changes: 52 additions & 27 deletions GVFS/GVFS.Virtualization/BlobSize/BlobSizes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,54 @@ public virtual void Initialize()
string folderPath = Path.GetDirectoryName(this.databasePath);
this.fileSystem.CreateDirectory(folderPath);

try
{
this.InitializeDatabase();
}
catch (SqliteException ex) when (ex.SqliteErrorCode == SqliteErrorCodes.Corrupt || ex.SqliteErrorCode == SqliteErrorCodes.NotADatabase)
{
EventMetadata metadata = this.CreateEventMetadata(ex);
metadata.Add("SqliteErrorCode", ex.SqliteErrorCode);
this.tracer.RelatedWarning(metadata, $"{nameof(BlobSizes)}.{nameof(this.Initialize)}: database corrupt, deleting and recreating");

SqliteConnection.ClearAllPools();
this.DeleteDatabaseFiles();
this.InitializeDatabase();
}

this.flushDataThread = new Thread(this.FlushDbThreadMain);
this.flushDataThread.IsBackground = true;
this.flushDataThread.Start();
}

public virtual void Shutdown()
{
this.isStopping = true;
this.wakeUpFlushThread.Set();
this.flushDataThread.Join();
}

public virtual void AddSize(Sha1Id sha, long size)
{
this.queuedSizes.Enqueue(new BlobSize(sha, size));
}

public virtual void Flush()
{
this.wakeUpFlushThread.Set();
}

public void Dispose()
{
if (this.wakeUpFlushThread != null)
{
this.wakeUpFlushThread.Dispose();
this.wakeUpFlushThread = null;
}
}

private void InitializeDatabase()
{
using (SqliteConnection connection = new SqliteConnection(this.sqliteConnectionString))
{
connection.Open();
Expand Down Expand Up @@ -125,36 +173,13 @@ public virtual void Initialize()
createTableCommand.ExecuteNonQuery();
}
}

this.flushDataThread = new Thread(this.FlushDbThreadMain);
this.flushDataThread.IsBackground = true;
this.flushDataThread.Start();
}

public virtual void Shutdown()
private void DeleteDatabaseFiles()
{
this.isStopping = true;
this.wakeUpFlushThread.Set();
this.flushDataThread.Join();
}

public virtual void AddSize(Sha1Id sha, long size)
{
this.queuedSizes.Enqueue(new BlobSize(sha, size));
}

public virtual void Flush()
{
this.wakeUpFlushThread.Set();
}

public void Dispose()
{
if (this.wakeUpFlushThread != null)
{
this.wakeUpFlushThread.Dispose();
this.wakeUpFlushThread = null;
}
this.fileSystem.TryDeleteFile(this.databasePath);
this.fileSystem.TryDeleteFile(this.databasePath + "-wal");
this.fileSystem.TryDeleteFile(this.databasePath + "-shm");
}

private void FlushDbThreadMain()
Expand Down
Loading