7

Is there any way to increase the chrome.storage.sync.QUOTA_BYTES_PER_ITEM ?

For me, the default 4096 Bytes is a little bit short.

I tried to execute

chrome.storage.sync.QUOTA_BYTES_PER_ITEM = 8192;

However, it seems that the actual limit doesn't change.

How can I do this?

Pengin
  • 771
  • 1
  • 10
  • 16
  • Note: as of 1/15/16, the (default) value of QUOTA_BYTES_PER_ITEM is now 8K. – Craig S. Anderson Jan 15 '16 at 23:25
  • I've created a [small class](https://github.com/kdzwinel/Context/blob/master/js/classes/HugeStorageSync.class.js) for my extension that handles saving and retrieving strings longer than `QUOTA_BYTES_PER_ITEM` from `chrome.storage.sync`. I based my code on a snippet contributed by @apsillers. – Konrad Dzwinel Jul 18 '13 at 11:24

3 Answers3

10

No, QUOTA_BYTES_PER_ITEM is there for reference only; it is not a settable value. You could use the value of QUOTA_BYTES_PER_ITEM to split an item up into multiple items, though:

function syncStore(key, objectToStore, callback) {
    var jsonstr = JSON.stringify(objectToStore);
    var i = 0;
    var storageObj = {};

    // split jsonstr into chunks and store them in an object indexed by `key_i`
    while(jsonstr.length > 0) {
        var index = key + "_" + i++;

        // since the key uses up some per-item quota, see how much is left for the value
        // also trim off 2 for quotes added by storage-time `stringify`
        var valueLength = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - index.length - 2;

        // trim down segment so it will be small enough even when run through `JSON.stringify` again at storage time
        var segment = jsonstr.substr(0, valueLength);           
        while(JSON.stringify(segment).length > valueLength)
            segment = jsonstr.substr(0, --valueLength);

        storageObj[index] = segment;
        jsonstr = jsonstr.substr(valueLength);
    }

    // store all the chunks
    chrome.storage.sync.set(storageObj, callback);
}

Then write an analogous fetch function that fetches by key and glues the object back together.

apsillers
  • 112,806
  • 17
  • 235
  • 239
  • 1
    There seems to be an infinite loop in your code. Please update the last line of the `while` loop to `jsonstr = jsonstr.substr(chrome.storage.sync.QUOTA_BYTES_PER_ITEM);`. – Konrad Dzwinel Jul 18 '13 at 09:50
  • You will also need a `i++` somewhere. – Konrad Dzwinel Jul 18 '13 at 09:59
  • Please also note that when `chrome.storage.sync.QUOTA_BYTES_PER_ITEM` is being checked by Chrome it calculates length of the value being saved but also length of a **key**. Therefore, you need to use something like `chrome.storage.sync.QUOTA_BYTES_PER_ITEM - currentKeyLength` when using `substr`. – Konrad Dzwinel Jul 18 '13 at 10:29
  • @KonradDzwinel As you can tell, this was just something I threw together as a proof-of-concept. Thanks for the feedback; I've put your request changes into effect in the code. The first two errors were obvious careless mistakes, but I didn't think about the size of the key -- is that noted somewhere in the docs, or did you discover it yourself (or are you a Chromium dev)? – apsillers Jul 18 '13 at 13:30
  • I figured out that your code was only a guideline, but since I used it I reported back all the errors. As for the key length, it is [documented](https://developer.chrome.com/extensions/storage.html#property-sync-QUOTA_BYTES_PER_ITEM). However, I found out that I had to subtract 2 more bytes for the unknown reasons ([crbug](https://code.google.com/p/chromium/issues/detail?id=261572) I've created). – Konrad Dzwinel Jul 18 '13 at 13:37
  • @KonradDzwinel Aha, thanks! I'd guess the two bytes are for quotes. The docs say "*JSON stringification of its value*" -- the result of `JSON.stringify("foo")` is the five-character string `"foo"` (the input is three chars, but the output is five, because quotes are actually in the string itself). You could test this by checking for the two-byte inflation with numbers; if I'm correct, it should *not* occur, because `JSON.stringify(5)` is just `5`, not `"5"`. – apsillers Jul 18 '13 at 13:45
  • Your explanation makes sense - thanks for that! However, I don't think there is a way to test it - creating a number that takes `QUOTA_BYTES_PER_ITEM` bytes doesn't seem possible. – Konrad Dzwinel Jul 19 '13 at 07:22
  • @apsillers, Why is `QUOTA_BYTES_PER_ITEM` not a constant variable but a settable variable? – Pacerier Feb 13 '17 at 20:46
  • @Pacerier I'm not sure what your question is, exactly. Are you asking how, mechanically, the browser can establish a variable whose value cannot be changed? If so, see the `writable` option of [`Object.defineProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) Or are you asking why Google doesn't allow you change the size of their per-item limit on synced storage? This is because their server-side sync storage enforces this limit. If you want to change it... write Google an angry letter demanding more bigger sync space, maybe? `:)` – apsillers Feb 13 '17 at 21:58
  • @Pacerier For comparison, `storage.local` does not have any per-item limit, because Google isn't responsible for hosting it on their servers. You can make the items as big as your own hard drive can handle. – apsillers Feb 13 '17 at 22:01
  • As a heads up even if you do this, max quota bytes is 102K https://developer.chrome.com/extensions/storage#properties – adamscott Jan 26 '20 at 22:13
2

just modify answer of @apsilliers

function syncStore(key, objectToStore) {
    var jsonstr = JSON.stringify(objectToStore);
    var i = 0;
    var storageObj = {};

    // split jsonstr into chunks and store them in an object indexed by `key_i`
    while(jsonstr.length > 0) {
        var index = key + "_" + i++;

        // since the key uses up some per-item quota, see how much is left for the value
        // also trim off 2 for quotes added by storage-time `stringify`
        const maxLength = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - index.length - 2;
        var valueLength = jsonstr.length;
        if(valueLength > maxLength){
            valueLength = maxLength;
        }

        // trim down segment so it will be small enough even when run through `JSON.stringify` again at storage time
        //max try is QUOTA_BYTES_PER_ITEM to avoid infinite loop
        var segment = jsonstr.substr(0, valueLength); 
        for(let i = 0; i < chrome.storage.sync.QUOTA_BYTES_PER_ITEM; i++){
            const jsonLength = JSON.stringify(segment).length;
            if(jsonLength > maxLength){
                segment = jsonstr.substr(0, --valueLength);
            }else {
                break;
            }
        }

        storageObj[index] = segment;
        jsonstr = jsonstr.substr(valueLength);
    }

also function to read each partition and merge again

function syncGet(key, callback) {
    chrome.storage.sync.get(key, (data) => {
        console.log(data[key]);
        console.log(typeof data[key]);
        if(data != undefined && data != "undefined" && data != {} && data[key] != undefined && data[key] != "undefined"){
            const keyArr = new Array();
            for(let i = 0; i <= data[key].count; i++) {
                keyArr.push(`${data[key].prefix}${i}`)
            }   
            chrome.storage.sync.get(keyArr, (items) => {
                console.log(data)
                const keys = Object.keys( items );
                const length = keys.length;
                let results = "";
                if(length > 0){
                    const sepPos = keys[0].lastIndexOf("_");
                    const prefix = keys[0].substring(0, sepPos);
                    for(let x = 0; x < length; x ++){
                        results += items[`${prefix }_${x}`];
                    }
                    callback(JSON.parse(results));
                    return;
                }
                callback(undefined);
            
            });
        } else {
            callback(undefined);
        }
    });
}

it tested and it works for my case

uncle bob
  • 570
  • 1
  • 10
  • 21
  • 1
    Almost perfect, but some small typo. You forgot to call `chrome.storage.sync.set(storageObj)` on few last line of syncStore. Anyway, thx for quick snippet, +1. – Wappenull Mar 13 '22 at 10:06
1

this is a better version of @uncle bob's functions, working with manifest v3 (you can use it just like how you can use the normal sync.set or sync.get function)

NOTE: it only works with JSONs (arrays and objects) since a string shouldn't be that long

let browserServices;
if (typeof browser === "undefined") {
  browserServices = chrome;
} else {
  browserServices = browser;
}

function syncSet(obj = {}) {
  return new Promise((resolve, reject) => {
    var storageObj = {};
    for (let u = 0; u < Object.keys(obj).length; u++) {
      const key = Object.keys(obj)[u];
      const objectToStore = obj[key]
      var jsonstr = JSON.stringify(objectToStore);
      var i = 0;

      // split jsonstr into chunks and store them in an object indexed by `key_i`
      while (jsonstr.length > 0) {
        var index = key + "USEDTOSEPERATE" + i++;

        // since the key uses up some per-item quota, see how much is left for the value
        // also trim off 2 for quotes added by storage-time `stringify`
        const maxLength = browserServices.storage.sync.QUOTA_BYTES_PER_ITEM - index.length - 2;
        var valueLength = jsonstr.length;
        if (valueLength > maxLength) {
          valueLength = maxLength;
        }

        // trim down segment so it will be small enough even when run through `JSON.stringify` again at storage time
        //max try is QUOTA_BYTES_PER_ITEM to avoid infinite loop
        var segment = jsonstr.substring(0, valueLength);
        var jsonLength = JSON.stringify(segment).length;
        segment = jsonstr.substring(0, valueLength = (valueLength - (jsonLength - maxLength) - 1));
        for (let i = 0; i < browserServices.storage.sync.QUOTA_BYTES_PER_ITEM; i++) {
          jsonLength = JSON.stringify(segment).length;
          if (jsonLength > maxLength) {
            segment = jsonstr.substring(0, --valueLength);
          } else {
            break;
          }
        }

        storageObj[index] = segment;
        jsonstr = jsonstr.substring(valueLength, Infinity);
      }
    }
    chrome.storage.sync.set(storageObj).then(() => {
      resolve()
    })
  })
}

function syncGet(uniqueKeys = []) {
  return new Promise((resolve, reject) => {
    browserServices.storage.sync.get(null).then((data) => {
      const keyArr = Object.keys(data).filter(e => uniqueKeys.filter(j => e.indexOf(j) == 0).length > 0)
      browserServices.storage.sync.get(keyArr).then((items) => {
        var results = {};
        for (let i = 0; i < uniqueKeys.length; i++) {
          const uniqueKey = uniqueKeys[i];
          const keysFiltered = keyArr.filter(e => e.split("USEDTOSEPERATE")[0] == uniqueKey)
          if (keysFiltered.length > 0) {
            results[uniqueKey] = ""
            for (let x = 0; x < keysFiltered.length; x++) {
              results[uniqueKey] += items[`${keysFiltered[x]}`];
            }
            results[uniqueKey] = JSON.parse(results[uniqueKey])
          }
        }
        resolve(results)
      });
    });

  })
}

example of usage:

syncSet({
  "keyTest": ["a lot of text"],
  "keyTest1": ["a lot of text"]
}
)
syncGet(["keyTest","keyTest1"]).then(results=>console.log(results))
// {keyTest:["a lot of text"],keyTest1:["a lot of text"]}
Mehdi Mamas
  • 105
  • 1
  • 2
  • 9