"Disk I/O error" on .Net 9 Blazor WebAssembly App and IDBFS
After updating to .Net 9 we get "Disk I/O error". A read and write test with a simple text file in IDBFS was successful in .Net 9. See this thread:
https://github.com/ericsink/SQLitePCL.raw/issues/472#issuecomment-2477108935
I need LOTS more info. A sample that reproduces the problem. Etc.
I can't upload files from my work computer, but I can copy the code as text and describe the small modifications to a standard Blazor app template. Is that enough for you? I'll put it in step by step, it's not much.
Is that enough
I don't know, but it's moving in the right direction.
-
Create project from template:
-
Create "files.js" in "wwwroot" folder:
function mount(dotNet)
{
// Mount db
const dbFolder = '/data';
Blazor.runtime.Module.FS.mkdir(dbFolder);
Blazor.runtime.Module.FS.mount(Blazor.runtime.Module.FS.filesystems.IDBFS, {}, dbFolder);
Blazor.runtime.Module.FS.syncfs(true, async (err) =>
{
if (dotNet)
{
const hasError = err != null;
await dotNet.invokeMethodAsync('MountFinished', hasError);
}
});
}
function sync()
{
// Sync db
Blazor.runtime.Module.FS.syncfs(async (err) =>
{
});
}
- Content of csproj-file:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<EmccExtraLDFlags>-lidbfs.js</EmccExtraLDFlags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" PrivateAssets="all" />
<PackageReference Include="sqlite-net-sqlcipher" Version="1.9.172" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.10" />
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.10" />
<PackageReference Include="SQLitePCLRaw.lib.e_sqlcipher" Version="2.1.10" />
<PackageReference Include="SQLitePCLRaw.provider.e_sqlcipher" Version="2.1.10" />
</ItemGroup>
</Project>
- Add this to "index.html" in the "head" section:
<script src="files.js"></script>
- Content of "Home.razor":
@page "/"
@using System.IO
@using SQLite
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
@code {
private DotNetObjectReference<Home> dotNet;
// Injections
[Inject]
private IJSRuntime JsRuntime { get; set; }
protected override async void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
// Mount IDBFS
if (firstRender)
{
// Create .NET reference
dotNet = DotNetObjectReference.Create(this);
// Mount
await JsRuntime.InvokeVoidAsync("mount", dotNet);
}
}
[JSInvokable]
public async void MountFinished(bool hasError)
{
if (!hasError)
{
// Test
string filename = "/data/Test.txt";
// Create if not exists
if (File.Exists(filename))
Console.WriteLine("File already exists.");
else
{
Console.WriteLine("Create file.");
// Write file
File.WriteAllText(filename, "Hello persistent filesystem.");
// Sync with IndexedDB
await JsRuntime.InvokeVoidAsync("sync");
}
// Read
if (File.Exists(filename))
Console.WriteLine(File.ReadAllText(filename));
// Test database
string databaseFile = "/data/Test.sqlite";
// Db exists?
bool dbExists = File.Exists(databaseFile);
// Open or create database
SQLiteConnection connection = new SQLiteConnection(new SQLiteConnectionString(databaseFile, SQLiteOpenFlags.FullMutex | SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, true, "secret"));
connection.Execute("PRAGMA cipher_compatibility=3");
// If new create a table
if (!dbExists)
{
// Create test table
connection.Execute(
@"CREATE TABLE IF NOT EXISTS TEST (
ID TEXT NOT NULL,
NAME TEXT NOT NULL,
TEST TEXT,
PRIMARY KEY (ID, NAME))");
}
// Sync with IndexedDB
await JsRuntime.InvokeVoidAsync("sync");
}
}
}
If you then run this, you will get the "disk i/o error" when executing the "create table" sql.
I suggest changing from sqlite-net-sqlcipher to sqlite-net base, and then add a call to SQLitePCL.Batteries.Init().
Also, if you have the bundle_e_sqlcipher package, you don't need to have entries for the other three SQLitePCLRaw packages.
Did the same (or equivalent) test work under .NET 8?
Do you get the same results with this test if you change from SQLCipher to regular SQLite?
In .Net 8 this code runs without any problems (with a few adjustments in the Javascript).
I have now changed my project file as follows:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<EmccExtraLDFlags>-lidbfs.js</EmccExtraLDFlags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" PrivateAssets="all" />
<!--
<PackageReference Include="sqlite-net-sqlcipher" Version="1.9.172" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.10" />
-->
<PackageReference Include="sqlite-net-base" Version="1.9.172" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
</ItemGroup>
</Project>
Then I added "SQLitePCL.Batteries.Init();" before creating the connection. The basic version works, the cypher version generates the following exception:
SQLite.SQLiteException: disk I/O error at SQLite.SQLite3.Prepare2(sqlite3 db, String query) at SQLite.SQLiteCommand.Prepare() at SQLite.SQLiteCommand.ExecuteNonQuery() at SQLite.SQLiteConnection.Execute(String query, Object[] args) at BlazorApp4Sqlite.Pages.Home.MountFinished(Boolean hasError) in C:\Temp\Temp3\Test\BlazorApp4Sqlite\Pages\Home.razor:line 67 at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_1(Object state) at System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading.ThreadPool.BackgroundJobHandler()
Of course we want to continue using the cypher version.
OK, so this appears to be a problem with the build of e_sqlcipher for wasm. The e_sqlcipher builds are unsupported, but I'll take a look at the build configuration and see if I see an easy or obvious problem.
Thank you.
Any news about the problem?
Nothing significant yet. I verified that my build system is doing a net9 build for e_sqlcipher, which I wasn't sure about, but I haven't found time to dig deeper.
Happy new year. Any news?
I haven't had time to investigate this further. :-(