1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Redefine what the opfs-sahpool installation promise resolves to. Fix addCapacity(). Add utility methods to import/export files.

FossilOrigin-Name: 809c6f4de3653ad7a7751af45a7a0d6cb20c3ee3be80c69833c729242227d970
This commit is contained in:
stephan
2023-07-16 10:02:41 +00:00
parent 28d46cce0b
commit d0ae50411f
4 changed files with 99 additions and 57 deletions

View File

@@ -56,10 +56,10 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
const toss = sqlite3.util.toss; const toss = sqlite3.util.toss;
let vfsRegisterResult = undefined; let vfsRegisterResult = undefined;
/** /**
installOpfsSAHPoolVfs() asynchronously initializes the installOpfsSAHPoolVfs() asynchronously initializes the OPFS
OPFS SyncAccessHandle Pool VFS. It returns a Promise SyncAccessHandle Pool VFS. It returns a Promise which either
which either resolves to the sqlite3 object or rejects resolves to a utility object described below or rejects with an
with an Error value. Error value.
Initialization of this VFS is not automatic because its Initialization of this VFS is not automatic because its
registration requires that it lock all resources it registration requires that it lock all resources it
@@ -75,6 +75,12 @@ let vfsRegisterResult = undefined;
resolved or rejected Promise. If called while the first call is resolved or rejected Promise. If called while the first call is
still pending resolution, a rejected promise with a descriptive still pending resolution, a rejected promise with a descriptive
error is returned. error is returned.
On success, the resulting Promise resolves to a utility object
which can be used to query and manipulate the pool. Its API is...
TODO
*/ */
sqlite3.installOpfsSAHPoolVfs = async function(){ sqlite3.installOpfsSAHPoolVfs = async function(){
if(sqlite3===vfsRegisterResult) return Promise.resolve(sqlite3); if(sqlite3===vfsRegisterResult) return Promise.resolve(sqlite3);
@@ -112,8 +118,11 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
vfsRegisterResult = err; vfsRegisterResult = err;
return Promise.reject(err); return Promise.reject(err);
}; };
/** The PoolUtil object will be the result of the
resolved Promise. */
const PoolUtil = Object.create(null);
const promiseResolve = const promiseResolve =
()=>Promise.resolve(vfsRegisterResult = sqlite3); ()=>Promise.resolve(vfsRegisterResult = PoolUtil);
// Config opts for the VFS... // Config opts for the VFS...
const SECTOR_SIZE = 4096; const SECTOR_SIZE = 4096;
const HEADER_MAX_PATH_SIZE = 512; const HEADER_MAX_PATH_SIZE = 512;
@@ -184,15 +193,14 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
to the new capacity. to the new capacity.
*/ */
addCapacity: async function(n){ addCapacity: async function(n){
const cap = this.getCapacity(); for(let i = 0; i < n; ++i){
for(let i = cap; i < cap+n; ++i){
const name = getRandomName(); const name = getRandomName();
const h = await this.dirHandle.getFileHandle(name, {create:true}); const h = await this.dirHandle.getFileHandle(name, {create:true});
const ah = await h.createSyncAccessHandle(); const ah = await h.createSyncAccessHandle();
this.mapSAHToName.set(ah,name); this.mapSAHToName.set(ah,name);
this.setAssociatedPath(ah, '', 0); this.setAssociatedPath(ah, '', 0);
} }
return i; return this.getCapacity();
}, },
/** /**
Removes n entries from the pool's current capacity Removes n entries from the pool's current capacity
@@ -399,14 +407,19 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
const rc = this.$error; const rc = this.$error;
this.$error = undefined; this.$error = undefined;
return rc; return rc;
},
nextAvailableSAH: function(){
const [rc] = this.availableSAH.keys();
return rc;
} }
})/*SAHPool*/; })/*SAHPool*/;
sqlite3.SAHPool = SAHPool/*only for testing*/; //sqlite3.SAHPool = SAHPool/*only for testing*/;
/** /**
Impls for the sqlite3_io_methods methods. Maintenance reminder: Impls for the sqlite3_io_methods methods. Maintenance reminder:
members are in alphabetical order to simplify finding them. members are in alphabetical order to simplify finding them.
*/ */
const ioSyncWrappers = { const ioMethods = {
xCheckReservedLock: function(pFile,pOut){ xCheckReservedLock: function(pFile,pOut){
log('xCheckReservedLock'); log('xCheckReservedLock');
SAHPool.storeErr(); SAHPool.storeErr();
@@ -523,13 +536,13 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
return capi.SQLITE_IOERR; return capi.SQLITE_IOERR;
} }
} }
}/*ioSyncWrappers*/; }/*ioMethods*/;
/** /**
Impls for the sqlite3_vfs methods. Maintenance reminder: members Impls for the sqlite3_vfs methods. Maintenance reminder: members
are in alphabetical order to simplify finding them. are in alphabetical order to simplify finding them.
*/ */
const vfsSyncWrappers = { const vfsMethods = {
xAccess: function(pVfs,zName,flags,pOut){ xAccess: function(pVfs,zName,flags,pOut){
log(`xAccess ${wasm.cstrToJs(zName)}`); log(`xAccess ${wasm.cstrToJs(zName)}`);
SAHPool.storeErr(); SAHPool.storeErr();
@@ -597,7 +610,7 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
// File not found so try to create it. // File not found so try to create it.
if(SAHPool.getFileCount() < SAHPool.getCapacity()) { if(SAHPool.getFileCount() < SAHPool.getCapacity()) {
// Choose an unassociated OPFS file from the pool. // Choose an unassociated OPFS file from the pool.
[sah] = SAHPool.availableSAH.keys(); sah = SAHPool.nextAvailableSAH();
SAHPool.setAssociatedPath(sah, path, flags); SAHPool.setAssociatedPath(sah, path, flags);
}else{ }else{
// File pool is full. // File pool is full.
@@ -621,7 +634,7 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
return capi.SQLITE_CANTOPEN; return capi.SQLITE_CANTOPEN;
} }
}/*xOpen()*/ }/*xOpen()*/
}/*vfsSyncWrappers*/; }/*vfsMethods*/;
if(dVfs){ if(dVfs){
/* Inherit certain VFS members from the default VFS, /* Inherit certain VFS members from the default VFS,
@@ -631,7 +644,7 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
} }
if(!opfsVfs.$xRandomness){ if(!opfsVfs.$xRandomness){
/* If the default VFS has no xRandomness(), add a basic JS impl... */ /* If the default VFS has no xRandomness(), add a basic JS impl... */
vfsSyncWrappers.xRandomness = function(pVfs, nOut, pOut){ vfsMethods.xRandomness = function(pVfs, nOut, pOut){
const heap = wasm.heap8u(); const heap = wasm.heap8u();
let i = 0; let i = 0;
for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF; for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF;
@@ -639,7 +652,7 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
}; };
} }
if(!opfsVfs.$xSleep){ if(!opfsVfs.$xSleep){
vfsSyncWrappers.xSleep = (pVfs,ms)=>0; vfsMethods.xSleep = (pVfs,ms)=>0;
} }
/** /**
@@ -669,6 +682,58 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
if(true!==apiVersionCheck){ if(true!==apiVersionCheck){
return promiseReject(apiVersionCheck); return promiseReject(apiVersionCheck);
} }
PoolUtil.$SAHPool = SAHPool/* ONLY for testing and debugging */;
PoolUtil.addCapacity = async (n)=>SAHPool.addCapacity(n);
PoolUtil.reduceCapacity = async (n)=>SAHPool.reduceCapacity(n);
PoolUtil.getCapacity = SAHPool.getCapacity.bind(SAHPool);
PoolUtil.getActiveFileCount = SAHPool.getFileCount.bind(SAHPool);
/**
Synchronously reads the contents of the given file into a
Uint8Array and returns it. This will throw if the given name is
not currently in active use or on I/O error.
*/
PoolUtil.exportFile = function(name){
const sah = SAHPool.mapPathToSAH.get(name) || toss("File not found:",name);
const n = sah.getSize() - HEADER_OFFSET_DATA;
const b = new Uint8Array(n>=0 ? n : 0);
if(n>0) sah.read(b, {at: HEADER_OFFSET_DATA});
return b;
};
/**
The counterpart of exportFile(), this imports the contents of an
SQLite database, provided as a byte array, under the given name,
overwriting any existing content. Throws if the pool has no
available file slots, on I/O error, or if the input does not
appear to be a database. In the latter case, only a cursory
examination is made.
Note that this routine is _only_ for importing database files,
not arbitrary files, the reason being that this VFS will
automatically clean up any non-database files so importing them
is pointless.
Returns undefined.
*/
PoolUtil.importDb = function(name, bytes){
const n = bytes.byteLength;
if(n<512 || n%512!=0){
toss("Byte array size is invalid for an SQLite db.");
}
const header = "SQLite format 3";
for(let i = 0; i < header.length; ++i){
if( header.charCodeAt(i) !== bytes[i] ){
toss("Input does not contain an SQLite database header.");
}
}
const sah = SAHPool.mapPathToSAH.get(name)
|| SAHPool.nextAvailableSAH()
|| toss("No available handles to import to.");
sah.write(bytes, {at: HEADER_OFFSET_DATA});
SAHPool.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB);
};
return SAHPool.isReady = SAHPool.reset().then(async ()=>{ return SAHPool.isReady = SAHPool.reset().then(async ()=>{
if(SAHPool.$error){ if(SAHPool.$error){
throw SAHPool.$error; throw SAHPool.$error;
@@ -678,12 +743,11 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
} }
//log("vfs list:",capi.sqlite3_js_vfs_list()); //log("vfs list:",capi.sqlite3_js_vfs_list());
sqlite3.vfs.installVfs({ sqlite3.vfs.installVfs({
io: {struct: opfsIoMethods, methods: ioSyncWrappers}, io: {struct: opfsIoMethods, methods: ioMethods},
vfs: {struct: opfsVfs, methods: vfsSyncWrappers}, vfs: {struct: opfsVfs, methods: vfsMethods}
applyArgcCheck: true
}); });
log("opfsVfs",opfsVfs,"opfsIoMethods",opfsIoMethods); //log("opfsVfs",opfsVfs,"opfsIoMethods",opfsIoMethods);
log("vfs list:",capi.sqlite3_js_vfs_list()); //log("vfs list:",capi.sqlite3_js_vfs_list());
if(sqlite3.oo1){ if(sqlite3.oo1){
const OpfsSAHPoolDb = function(...args){ const OpfsSAHPoolDb = function(...args){
const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args); const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...args);
@@ -691,39 +755,14 @@ sqlite3.installOpfsSAHPoolVfs = async function(){
sqlite3.oo1.DB.dbCtorHelper.call(this, opt); sqlite3.oo1.DB.dbCtorHelper.call(this, opt);
}; };
OpfsSAHPoolDb.prototype = Object.create(sqlite3.oo1.DB.prototype); OpfsSAHPoolDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
OpfsSAHPoolDb.addPoolCapacity = async (n)=>SAHPool.addCapacity(n); OpfsSAHPoolDb.PoolUtil;
OpfsSAHPoolDb.reducePoolCapacity = async (n)=>SAHPool.reduceCapacity(n);
OpfsSAHPoolDb.getPoolCapacity = ()=>SAHPool.getCapacity();
OpfsSAHPoolDb.getPoolUsage = ()=>SAHPool.getFileCount();
sqlite3.oo1.OpfsSAHPoolDb = OpfsSAHPoolDb; sqlite3.oo1.OpfsSAHPoolDb = OpfsSAHPoolDb;
sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql( sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql(
opfsVfs.pointer, opfsVfs.pointer,
function(oo1Db, sqlite3){ function(oo1Db, sqlite3){
sqlite3.capi.sqlite3_exec(oo1Db, [ sqlite3.capi.sqlite3_exec(oo1Db, [
/* As of July 2023, the PERSIST journal mode on OPFS is /* See notes in sqlite3-vfs-opfs.js */
somewhat slower than DELETE or TRUNCATE (it was faster
before Chrome version 108 or 109). TRUNCATE and DELETE
have very similar performance on OPFS.
Roy Hashimoto notes that TRUNCATE and PERSIST modes may
decrease OPFS concurrency because multiple connections
can open the journal file in those modes:
https://github.com/rhashimoto/wa-sqlite/issues/68
Given that, and the fact that testing has not revealed
any appreciable difference between performance of
TRUNCATE and DELETE modes on OPFS, we currently (as of
2023-07-13) default to DELETE mode.
*/
"pragma journal_mode=DELETE;", "pragma journal_mode=DELETE;",
/*
OPFS benefits hugely from cache on moderate/large
speedtest1 --size 50 and --size 100 workloads. We
currently rely on setting a non-default cache size when
building sqlite3.wasm. If that policy changes, the cache
can be set here.
*/
"pragma cache_size=-16384;" "pragma cache_size=-16384;"
], 0, 0, 0); ], 0, 0, 0);
} }

View File

@@ -107,7 +107,10 @@
const S = globalThis.S = sqlite3; const S = globalThis.S = sqlite3;
log("Loaded speedtest1 module. Setting up..."); log("Loaded speedtest1 module. Setting up...");
if(S.installOpfsSAHPoolVfs){ if(S.installOpfsSAHPoolVfs){
await S.installOpfsSAHPoolVfs().catch(e=>{ await S.installOpfsSAHPoolVfs().then(P=>{
S.SAHPoolUtil = P;
//return P.addCapacity(5).then(log("pool capacity:",P.getCapacity()));;
}).catch(e=>{
logErr("Error setting up opfs-sahpool:",e.message); logErr("Error setting up opfs-sahpool:",e.message);
}); });
} }

View File

@@ -1,5 +1,5 @@
C Minor\scleanups\sin\sthe\sopfs-sahpool\sVFS. C Redefine\swhat\sthe\sopfs-sahpool\sinstallation\spromise\sresolves\sto.\sFix\saddCapacity().\sAdd\sutility\smethods\sto\simport/export\sfiles.
D 2023-07-15T21:08:48.986 D 2023-07-16T10:02:41.870
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -502,7 +502,7 @@ F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b17386
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
F ext/wasm/api/sqlite3-opfs-async-proxy.js 961bbc3ccc1fa4e91d6519a96e8811ad7ae60173bd969fee7775dacb6eee1da2 F ext/wasm/api/sqlite3-opfs-async-proxy.js 961bbc3ccc1fa4e91d6519a96e8811ad7ae60173bd969fee7775dacb6eee1da2
F ext/wasm/api/sqlite3-v-helper.js e5c202a9ecde9ef818536d3f5faf26c03a1a9f5192b1ddea8bdabf30d75ef487 F ext/wasm/api/sqlite3-v-helper.js e5c202a9ecde9ef818536d3f5faf26c03a1a9f5192b1ddea8bdabf30d75ef487
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.js 0032097c168c8fe7e753abc5b35e65323116d04b0dbaaa97176604660b7bb98c F ext/wasm/api/sqlite3-vfs-opfs-sahpool.js 0e982cc4f1b9ed4786086d1115e740b7efd628de5cbaf16caf3a71913f91241b
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js a5c3195203e6085d7aa89fae4b84cf3f3eec4ff4f928c6d0e5d3ef8b14cbc1c0 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js a5c3195203e6085d7aa89fae4b84cf3f3eec4ff4f928c6d0e5d3ef8b14cbc1c0
F ext/wasm/api/sqlite3-wasm.c 12a096d8e58a0af0589142bae5a3c27a0c7e19846755a1a37d2c206352fbedda F ext/wasm/api/sqlite3-wasm.c 12a096d8e58a0af0589142bae5a3c27a0c7e19846755a1a37d2c206352fbedda
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
@@ -540,7 +540,7 @@ F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd84223150
F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d
F ext/wasm/speedtest1-wasmfs.mjs ac5cadbf4ffe69e9eaac8b45e8523f030521e02bb67d654c6eb5236d9c456cbe F ext/wasm/speedtest1-wasmfs.mjs ac5cadbf4ffe69e9eaac8b45e8523f030521e02bb67d654c6eb5236d9c456cbe
F ext/wasm/speedtest1-worker.html bbcf1e7fd79541040c1a7ca2ebf1cb7793ddaf9900d6bde1784148f11b807c34 F ext/wasm/speedtest1-worker.html bbcf1e7fd79541040c1a7ca2ebf1cb7793ddaf9900d6bde1784148f11b807c34
F ext/wasm/speedtest1-worker.js 554b0985f791758e40ff2b1a04b771e315ab84b4e26b4b8a1c7a5ba968086c45 F ext/wasm/speedtest1-worker.js faa4a06ec21921aaa0e0b672a94b56037da837e16732bdd6545b99f1cadbb32e
F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
@@ -2044,8 +2044,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 41bf1fe31f2f3d0daa2bac25dc57262a4b90f22fed6fa97e4e92467c32ae02dc P 279e09070918dab7b60c39179ebb7eb931ca6bd4e589b414f436740499a2f910
R a6cacf00d5cb9e3eccf543ab6eefecd8 R 3700fe28ac7e053b813d4612e5083eb3
U stephan U stephan
Z d51af2a044894ea09e404a3df277218e Z 2285d7e58406d05c66733e55ed60721b
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
279e09070918dab7b60c39179ebb7eb931ca6bd4e589b414f436740499a2f910 809c6f4de3653ad7a7751af45a7a0d6cb20c3ee3be80c69833c729242227d970