Skip to content

Commit 9b9fc59

Browse files
Merge pull request #80 from contributorpw/edits
Add cache/chunky-cache
2 parents 3c72fa3 + 4c0f269 commit 9b9fc59

File tree

7 files changed

+280
-0
lines changed

7 files changed

+280
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"timeZone": "Europe/Moscow",
3+
"dependencies": {},
4+
"exceptionLogging": "STACKDRIVER",
5+
"runtimeVersion": "V8"
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "standalone"
3+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* The fork of
3+
* https://gist.github.com/pilbot/9d0567ef1daf556449fb,
4+
* https://gist.github.com/glade-at-gigwell/4e080771d685fbf1908edbd98eb2d88c
5+
*/
6+
7+
/**
8+
* Using the Google Apps Script Cache Service for objects above 100Kb
9+
*/
10+
class ChunkyCache {
11+
/**
12+
*
13+
* @param {GoogleAppsScript.Cache.Cache} cache
14+
*/
15+
constructor(cache) {
16+
this.cache = cache || CacheService.getScriptCache();
17+
this.chunkSize = 100 * 1024;
18+
}
19+
/**
20+
* Gets the cached value for the given key, or null if none is found.
21+
* https://developers.google.com/apps-script/reference/cache/cache#getkey
22+
*
23+
* @param {string} key
24+
* @returns {any} A JSON.parse result
25+
*/
26+
get(key) {
27+
const superKeyjson = this.cache.get(key);
28+
if (superKeyjson === null) return null;
29+
const superKey = JSON.parse(superKeyjson);
30+
const cache = this.cache.getAll(superKey.chunks);
31+
const chunks = superKey.chunks.map((key) => cache[key]);
32+
if (
33+
chunks.every(function (c) {
34+
return c !== undefined;
35+
})
36+
) {
37+
return JSON.parse(chunks.join(''));
38+
}
39+
}
40+
/**
41+
* Adds a key/value pair to the cache.
42+
* https://developers.google.com/apps-script/reference/cache/cache#putkey,-value
43+
*
44+
* @param {string} key
45+
* @param {string} value
46+
* @param {number} expirationInSeconds
47+
*/
48+
put(key, value, expirationInSeconds = 600) {
49+
const json = JSON.stringify(value);
50+
const chunks = [];
51+
const chunkValues = {};
52+
let index = 0;
53+
while (index < json.length) {
54+
const chunkKey = key + '_' + index;
55+
chunks.push(chunkKey);
56+
chunkValues[chunkKey] = json.substr(index, this.chunkSize);
57+
index += this.chunkSize;
58+
}
59+
const superKey = {
60+
chunkSize: this.chunkSize,
61+
chunks: chunks,
62+
length: json.length,
63+
};
64+
chunkValues[key] = JSON.stringify(superKey);
65+
this.cache.putAll(chunkValues, expirationInSeconds);
66+
}
67+
/**
68+
* Removes an entry from the cache using the given key.
69+
*
70+
* @returns {null}
71+
*/
72+
remove(key) {
73+
const superKeyjson = this.cache.get(key);
74+
if (superKeyjson !== null) {
75+
const superKey = JSON.parse(superKeyjson);
76+
this.cache.removeAll([...superKey.chunks, key]);
77+
}
78+
return null;
79+
}
80+
}
81+
82+
/**
83+
* Using the Google Apps Script Cache Service for blobs
84+
*/
85+
class BlobCache extends ChunkyCache {
86+
/**
87+
* Extends of ChunkyCache
88+
*/
89+
constructor(cache) {
90+
super(cache);
91+
this.splitter = '1c16c2eb-a4a7-4cac-bf79-064cedfbb346';
92+
this.defaultName = '772fff0c-4207-4893-834c-aec73c498eeb';
93+
this.prefixSize = 250;
94+
}
95+
/**
96+
* Adds a key/blob pair to the cache.
97+
*
98+
* @param {string} key
99+
* @param {GoogleAppsScript.Base.Blob | GoogleAppsScript.Base.BlobSource} blob
100+
* @param {number} expirationInSeconds
101+
*/
102+
putBlob(key, blob, expirationInSeconds = 600) {
103+
let name = blob.getName();
104+
if (name === null) name = this.defaultName;
105+
const contentType = blob.getContentType();
106+
const prefix = [name, this.splitter, contentType, this.splitter]
107+
.join('')
108+
.padEnd(this.prefixSize, ' ');
109+
const data = prefix + Utilities.base64Encode(blob.getBytes());
110+
this.put(key, data, expirationInSeconds);
111+
}
112+
/**
113+
* Gets the cached blob for the given key, or null if none is found.
114+
*
115+
* @param {*} key
116+
*/
117+
getBlob(key) {
118+
const data = this.get(key);
119+
if (data !== null) {
120+
const prefix = data.slice(0, this.prefixSize).split(this.splitter);
121+
const blob = Utilities.newBlob('');
122+
blob.setBytes(Utilities.base64Decode(data.slice(this.prefixSize)));
123+
if (prefix[0] !== this.defaultName) blob.setName(prefix[0]);
124+
blob.setContentType(prefix[2]);
125+
return blob;
126+
}
127+
return null;
128+
}
129+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
class ChunkyCache {
2+
constructor(cache) {
3+
this.cache = cache || CacheService.getScriptCache(), this.chunkSize = 102400;
4+
}
5+
get(superKeyjson) {
6+
superKeyjson = this.cache.get(superKeyjson);
7+
if (null === superKeyjson) return null;
8+
const superKey = JSON.parse(superKeyjson), cache = this.cache.getAll(superKey.chunks), chunks = superKey.chunks.map(key => cache[key]);
9+
return chunks.every(function(c) {
10+
return void 0 !== c;
11+
}) ? JSON.parse(chunks.join("")) : void 0;
12+
}
13+
put(key, superKey, expirationInSeconds = 600) {
14+
const json = JSON.stringify(superKey), chunks = [], chunkValues = {};
15+
let index = 0;
16+
for (;index < json.length; ) {
17+
var chunkKey = key + "_" + index;
18+
chunks.push(chunkKey), chunkValues[chunkKey] = json.substr(index, this.chunkSize),
19+
index += this.chunkSize;
20+
}
21+
superKey = {
22+
chunkSize: this.chunkSize,
23+
chunks: chunks,
24+
length: json.length
25+
};
26+
chunkValues[key] = JSON.stringify(superKey), this.cache.putAll(chunkValues, expirationInSeconds);
27+
}
28+
remove(key) {
29+
var superKey = this.cache.get(key);
30+
return null !== superKey && (superKey = JSON.parse(superKey), this.cache.removeAll([ ...superKey.chunks, key ])),
31+
null;
32+
}
33+
}
34+
35+
class BlobCache extends ChunkyCache {
36+
constructor(cache) {
37+
super(cache), this.splitter = "1c16c2eb-a4a7-4cac-bf79-064cedfbb346", this.defaultName = "772fff0c-4207-4893-834c-aec73c498eeb",
38+
this.prefixSize = 250;
39+
}
40+
putBlob(key, data, expirationInSeconds = 600) {
41+
let name = data.getName();
42+
null === name && (name = this.defaultName);
43+
const contentType = data.getContentType();
44+
data = [ name, this.splitter, contentType, this.splitter ].join("").padEnd(this.prefixSize, " ") + Utilities.base64Encode(data.getBytes());
45+
this.put(key, data, expirationInSeconds);
46+
}
47+
getBlob(prefix) {
48+
const data = this.get(prefix);
49+
if (null === data) return null;
50+
{
51+
prefix = data.slice(0, this.prefixSize).split(this.splitter);
52+
const blob = Utilities.newBlob("");
53+
return blob.setBytes(Utilities.base64Decode(data.slice(this.prefixSize))), prefix[0] !== this.defaultName && blob.setName(prefix[0]),
54+
blob.setContentType(prefix[2]), blob;
55+
}
56+
}
57+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: 'Put objects above 100KB to a Cache Service'
3+
date: '2021-07-16'
4+
description: 'Using the Google Apps Script Cache Service for objects/files above 100KB.'
5+
tags: ['cache']
6+
categories: ['snippets']
7+
images: ['./snippets/cache/chunky-cache/screenshot.png']
8+
---
9+
10+
## Using the Google Apps Script Cache Service for objects/files above 100KB
11+
12+
{{< toc >}}
13+
14+
![Using the Google Apps Script Cache Service for objects above 100KB](./screenshot.png)
15+
16+
### Snippet
17+
18+
This code is a fork of [pilbot/9d0567ef1daf556449fb](https://gist.github.com/pilbot/9d0567ef1daf556449fb) and [glade-at-gigwell/4e080771d685fbf1908edbd98eb2d88c](https://gist.github.com/glade-at-gigwell/4e080771d685fbf1908edbd98eb2d88c)
19+
20+
- {{< externalLink >}}
21+
- {{< commentLink >}}
22+
- {{< scrvizLink >}}
23+
24+
{{< codeFromFile "index.js" >}}
25+
26+
### Run it
27+
28+
{{< codeFromFile "run.js" >}}
29+
30+
### Minimized version
31+
32+
For quick use, you can use a more transparent version
33+
34+
{{< codeFromFile "index.min.js" >}}
35+
36+
{{< clipboard >}}

snippets/cache/chunky-cache/run.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/* eslint-disable no-console */
2+
/* global ChunkyCache BlobCache */
3+
4+
/**
5+
* Runs the snippet. Caches a 200KB string.
6+
*/
7+
function run() {
8+
const value = DriveApp.getFileById('19WGODj4pQ-VgwI2unZfQCxX25D4xaB4Q')
9+
.getBlob()
10+
.getDataAsString();
11+
new ChunkyCache().put('key', value);
12+
13+
const cacheValue = new ChunkyCache().get('key');
14+
new ChunkyCache().remove('key');
15+
console.log(value.length, cacheValue.length);
16+
}
17+
18+
/**
19+
* Runs the snippet. Caches an image.
20+
* Then creates a copy of this from the cache
21+
*/
22+
function runCacheImage() {
23+
const data = DriveApp.getFileById(
24+
'14Sm76a_dJI4eKtSbfCfDq4gVjcUzREE7'
25+
).getBlob();
26+
new BlobCache().putBlob('myfile1', data);
27+
28+
DriveApp.createFile(new BlobCache().getBlob('myfile1'));
29+
new BlobCache().remove('myfile1');
30+
}
31+
32+
/**
33+
* Runs the test. Caches a data from the Google Sheet.
34+
* Compares the original data and the cache data
35+
*/
36+
function runTest() {
37+
const sheet = SpreadsheetApp.openById(
38+
'19TlsK5ICOuzrv07OSw5n3KtYbTHn00p1iFY6QArBWTM'
39+
).getSheetByName('aka FuzzyMatch');
40+
const data = sheet.getDataRange().getValues();
41+
const chunky = new ChunkyCache(CacheService.getUserCache());
42+
chunky.put('Data', data);
43+
const check = chunky.get('Data');
44+
console.log(
45+
data.length,
46+
check.length,
47+
JSON.stringify(data) === JSON.stringify(check) // It's not work for Date values
48+
);
49+
}
97.4 KB
Loading

0 commit comments

Comments
 (0)