Re: Updating Quota API: Promise, Events and some more

On Tue, Aug 13, 2013 at 10:57 PM, Kinuko Yasuda <kinuko@chromium.org> wrote:

> Hi all,
>
> It's been a while since Quota API's FPWD (http://www.w3.org/TR/quota-api/)
> was published and we've gotten several requests/feedbacks so far.
> To address some of the requests and to gain more consensus, I'm thinking
> about making following changes to the Quota API:
>
> * Use Promises rather than callbacks
> * Add Events to notify webapps of important changes in the local storage
> space
> * Establish a way to get and set the storage types (temporary or
> persistent)
>   of each storage object
>
> This breaks compatibility in the existing implementation, but currently
> it's implemented only in Chrome behind the flag, so I hope/assume it'll be
> ok
> to make incompatible changes. I'm also strongly hoping these changes
> (and debate on them) help building more consensus.
>
> There're also some requests those are not (yet) addressed in this new
> draft:
>
> * More granularity in storage types or priorities, rather than sticking to
> the
>   rigid two types, so that webapps can indicate which data should be
> evicted
>   first / when.
> * Helper method to estimate 'actual' size of each storage object
> * Helper method to trigger GC/compaction on the local storage
>
> While they look nice-to-have in some situations but may also add more
> complexity in implementation, so I tentatively concluded that they can
> be put off until the next iteration.
>
> New draft needs some more polish but I'd like to get early feedback
> on the new draft.
>
> Detailed draft:
>
>   enum StorageType { "temporary", "persistent" };
>
>   partial interface Navigator {
>     readonly attribute StorageQuota storageQuota;
>   };
>
>   [NoInterfaceObject] interface StorageInfo {
>     unsigned long long usageInBytes;
>     unsigned long long quotaInBytes;
>   };
>
>   [NoInterfaceObject] interface StorageQuota {
>     readonly attribute StorageType[] supportedTypes;
>
>     Promise<StorageInfo> queryStorageInfo(StorageType type);
>     Promise<StorageInfo> requestQuota(StorageType type, unsigned long long
> newQuotaInBytes);
>
>     Promise<StorageType> getStorageType((IDBObjectStore or Database or
> Entry) object);
>     Promise<void> setStorageType((IDBObjectStore or Database or Entry)
> object, StorageType type);
>
>
For IndexedDB, an object store is (probably) too low a level to specify a
storage type; ISTM that a database makes more sense as the level of
granularity for specifying storage, since that avoids the complexity of a
store disappearing out from within a database. Was the use of object store
here intentional?

>From an API perspective, passing an IDBObjectStore instance also doesn't
make much sense as that sort of object is really a transaction-specific
handle. Before delving deeply, my gut reaction is that to fit into this API
you would need to pass an IDBDatabase connection object, and it would
generate an error unless called during a versionchange transaction (which
guarantees there are no other connections).

That still feels like an odd mix of two APIs. An approach that we (Moz +
Google) have talked about would be to extend the IDBFactory.open() call
with an options dictionary, e.g.

request = indexedDB.open({ name: ..., version: ..., storage: "temporary" });

On a tangent...

An open question is if the storage type (1) can be assigned only when an
IDB database is created, or (2) can be changed, allowing an IDB database to
be moved while retaining data, or (3) defines a namespace between origin
and database, i.e. "example.com" / "permanent" / "db-1" and "example.com" /
"temporary" / "db-1" co-exist as separate databases.

What are your thoughts on those 3 options with respect to other storage
systems?


>     StorageWatcher createStorageWatcher(StorageType type)
>   };
>
> This new draft uses string enums to specify storage types rather than
> separate attributes on navigator (e.g. navigator.temporaryStorage),
> mainly because some methods (like {get,set}StorageType do not fit well
> in split interface) and to preserve greater flexibility to add more storage
> types in a future. I'm open to discussions though.
>
>  supportedTypes are list of all StorageType's supported by the UA.
>
> * queryStorageInfo and requestQuota are Promise version of
>   queryUsageAndQuota and requestQuota, which is for querying the current
>   storage info (usage and quota) and requesting a new quota, respectively.
>   Both return the current (or updated) StorageInfo.
>
> * getStorageType and setStorageType are new methods which are intended to
>   work horizontally across multiple storage APIs. getStorageType(object)
>   returns the current storage type for the given storage object, and
>   setStorageType(object, type) changes the object's storage type.
>   They may fail if the storage backend of the object does not support
>   Quota API or does not support getting or setting (changing) storage
> types.
>
>   We're aware that this API may not work very well with FileSystem API(s),
> and
>   also will need coordination with IndexedDB. Feedback is strongly
> encouraged
>   here.
>
>
Since it doesn't seem like a good fit for FileSystem or (IMHO) IndexedDB,
can you give an example where this API does make sense? That might help
inform what the shape of such a horizontal API would be like that would
work with IDB, etc.



> * createStorageWatcher returns a new StorageWatcher object for the given
>   storage type, with which webapps can listen to important changes in the
>   local storage and can react appropriately (i.e. discard their
> lower-priority
>   cache data before UA-initiated merciless eviction happens).
>
> [NoInterfaceObject] interface createStorageWatcher {
>   attribute EventHandler onstoragelow;
>   attribute EventHandler onstorageok;
>   attribute EventHandler onstoragechange;
> };
>
> storagelow and storageok events are modeled after DEVICE_STORAGE_LOW
> and DEVICE_STORAGE_OK intents on android, to give more chances to
> webapps to adjust their local data quickly.
>
> * storagelow event is fired when the remaining storage space becomes lower
>   than 10% of all available space for the type, or before the quota backend
>   triggers eviction (for temporary case), whichever happens first.
>   This event must be also fired once when either one of these conditions
> has
>   been already met when a StorageWatcher is created.
>
> * storageok event is fired when the remaining available storage goes out of
>   'storagelow' state.
>
>
I should go look at the DEVICE_STORAGE_OK docs, but what actions would a
typical web app take in response to storageok? Is this to allow an app to
enter/exit a "low storage" state (i.e. restrict how much data it writes)?
Otherwise it doesn't seem particularly actionable.


> * storagechange event is fired about every 1 second (TBD) or every time
>   the quota backend detects the amount of remaining storage is changed,
>   whichever is least frequent. This event must be fired before storagelow
>   or storageok when the remaining space crosses the threshold.
>
>
This sounds like storagechange would fire every second even if storage
usage is constant - is that correct or am I misreading?

If the user performs a manual action (e.g. "clear browsing data"), I assume
this event would fire as well. Does a user-initiated action merit any other
sort of notification?





> Again, your review/feedback is really encouraged.  Thanks!
>
> Kinuko
>
>

Received on Wednesday, 14 August 2013 21:34:14 UTC