Instantly share code, notes, and snippets.

@jahands /get-b2-url.js Secret
Last active Oct 5, 2018

Embed
What would you like to do?
Cloudflare Workers snippets
// Use CF Argo for non-US/CA users
function getB2Url(request) {
let b2BackendUrl = 'https://b2.my-argo-enabled-domain.com/file';
let country = request.headers.get('CF-IPCountry')
if (country === 'US' || country === 'CA') {
b2BackendUrl = 'https://f001.backblazeb2.com/file';
}
return b2BackendUrl;
}
// Race GCS and B2
let gcsReq = new Request('https://storage.googleapis.com/myBucket' + url.pathname, event.request)
let b2Req = new Request(getB2Url(request) + '/myBucket' + url.pathname, event.request);
// Fetch from GCS and B2 with caching and log request stats
let reqStartTime = Date.now();
let gcsPromise = fetch(gcsReq, cfSettings);
let b2Promise = fetch(b2Req, cfSettings);
let response = await Promise.race([gcsPromise, b2Promise]);
if (response.ok) {
event.waitUntil(logResponse(event, response, (Date.now() - reqStartTime)));
return response;
}
// If the winner was bad, find the one that is good (if any)
response = await gcsPromise;
if (response.ok) {
event.waitUntil(logResponse(event, response, (Date.now() - reqStartTime)));
return response;
}
response = await b2Promise;
if (response.ok) {
event.waitUntil(logResponse(event, response, (Date.now() - reqStartTime)));
return response;
}
return response;
// Race GCS and B2
let gcsReq = new Request('https://storage.googleapis.com/bucketName' + url.pathname, event.request)
let b2Req = new Request(getB2Url(request) + '/bucketName' + url.pathname, event.request);
// Fetch from GCS and B2 with Cloudflare caching enabled
let gcsPromise = fetch(gcsReq, cfSettings);
let b2Promise = fetch(b2Req, cfSettings);
let response = await Promise.race([gcsPromise, b2Promise]);
if (response.ok) {
return response;
}
// If the winner was bad, find the one that is good (if any)
response = await gcsPromise;
if (response.ok) {
return response;
}
response = await b2Promise;
if (response.ok) {
return response;
}
// The request failed/doesn't exist
return response;
// Globals
let stats = {};
let statsLock = false;
let statsLastSendTime = Date.now();
let durationLastSendTime = Date.now();
// Log responses to StatHat.com
async function logResponse(event, response, duration) {
let url = new URL(response.url);
let limit = 25; // Submit stats every this many requests
let backend = url.hostname;
let cacheStatus = response.headers.get('CF-Cache-Status')
if (cacheStatus === 'EXPIRED') {
cacheStatus = 'MISS' // This is basically a miss I think.
}
// Only track 200 and 404
if (response.status < 200 || response.status > 299) {
return;
}
// Only track hits or misses, not Expired.
if (['HIT', 'MISS'].indexOf(cacheStatus) === -1) {
return;
}
if (stats[backend] === undefined) {
stats[backend] = {};
}
if (stats[backend][response.status] === undefined) {
stats[backend][response.status] = 1;
} else {
stats[backend][response.status]++;
}
// Send every X requests, unless locked or stats were last sent less than 5 seconds ago.
if (stats[backend][response.status] >= limit && !statsLock && ((Date.now() - statsLastSendTime) >= 5000)) {
// Idk if this helps anything or not, but the idea is to not double-count stats.
statsLock = true;
let statCount = stats[backend][response.status];
stats[backend][response.status] -= statCount;
statsLastSendTime = Date.now();
statsLock = false;
let statName = backend + '_' + response.status + '_' + cacheStatus;
event.waitUntil(fetch('https://api.stathat.com/ez?stat=' + statName + '&ezkey=xyz&count=' + statCount));
}
if ((Date.now() - durationLastSendTime) >= 1000 && cacheStatus === 'MISS') {
event.waitUntil(fetch('https://api.stathat.com/ez?stat=' + url.hostname + '_TIME' + '&ezkey=xyz&value=' + duration));
durationLastSendTime = Date.now();
}
}
// Fetch from GCS and B2 with caching
let reqStartTime = Date.now();
let gcsPromise = fetch(gcsReq, cfSettings);
let b2Promise = fetch(b2Req, cfSettings);
let response = await Promise.race([gcsPromise, b2Promise]);
if (response.ok) {
event.waitUntil(logResponse(event, response, (Date.now() - reqStartTime)));
return response;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment