Skip to content

Commit

Permalink
feat: track layer resolution header (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
vasco-santos authored Oct 14, 2022
1 parent f7f348c commit 08f3a6d
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 24 deletions.
3 changes: 2 additions & 1 deletion packages/edge-gateway/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export const CF_CACHE_MAX_OBJECT_SIZE = 512 * Math.pow(1024, 2) // 512MB to byte
export const RESOLUTION_LAYERS = {
CDN: 'cdn',
DOTSTORAGE_RACE: 'dotstorage-race',
PUBLIC_RACE: 'public-race'
PUBLIC_RACE_L1: 'public-race-l1',
PUBLIC_RACE_L2: 'public-race-l2'
}

export const RESOLUTION_IDENTIFIERS = {
Expand Down
41 changes: 25 additions & 16 deletions packages/edge-gateway/src/gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ import {

/**
* @typedef {import('./env').Env} Env
* @typedef {'cdn' | 'dotstorage-race' | 'public-race'} ResolutionLayer
* @typedef {'cdn' | 'dotstorage-race' | 'public-race-l1' | 'public-race-l2'} ResolutionLayer
*
* @typedef {import('ipfs-gateway-race').GatewayResponse} GatewayResponse
* @typedef {import('ipfs-gateway-race').GatewayResponsePromise} GatewayResponsePromise
* @typedef {{ value: GatewayResponse & { duration: number} }} GatewayRaceResponses
*
* @typedef {Object} ProxiedResponse
* @typedef {Object} ProxiedCDNResponse
* @property {Response} response
* @property {string} resolutionIdentifier
*
* @typedef {Object} ProxiedLayeredResponse
* @property {Response} response
* @property {ResolutionLayer} resolutionLayer
* @property {string} url
*/

/**
Expand Down Expand Up @@ -82,7 +87,8 @@ export async function gatewayGet (request, env, ctx) {
// 3rd layer resolution - Public Gateways race
const {
response: winnerGwResponse,
url: winnerUrl
url: winnerUrl,
resolutionLayer: raceResolutionLayer
} = await getFromGatewayRacer(cid, pathname, getHeaders(request), env, ctx)

// Validation layer - resource CID
Expand Down Expand Up @@ -114,7 +120,7 @@ export async function gatewayGet (request, env, ctx) {

return getResponseWithCustomHeaders(
winnerGwResponse,
RESOLUTION_LAYERS.PUBLIC_RACE,
raceResolutionLayer,
winnerUrl
)
}
Expand All @@ -125,7 +131,7 @@ export async function gatewayGet (request, env, ctx) {
* @param {Request} request
* @param {Env} env
* @param {Cache} cache
* @return {Promise<ProxiedResponse | undefined>}
* @return {Promise<ProxiedCDNResponse | undefined>}
*/
async function getFromCdn (request, env, cache) {
// Should skip cache if instructed by headers
Expand All @@ -143,7 +149,7 @@ async function getFromCdn (request, env, cache) {

// @ts-ignore p-any Promise types differ from CF promise types
const res = await pAny(cdnRequests, {
filter: (/** @type {ProxiedResponse} */ res) => !!res
filter: (/** @type {ProxiedCDNResponse} */ res) => !!res
})
return res
} catch (err) {
Expand All @@ -159,9 +165,10 @@ async function getFromCdn (request, env, cache) {
* @param {Request} request
* @param {string} cid
* @param {{ pathname?: string}} [options]
* @return {Promise<ProxiedResponse | undefined>}
* @return {Promise<ProxiedCDNResponse | undefined>}
*/
async function getFromDotstorage (request, cid, { pathname = '' } = {}) {
async function getFromDotstorage (request, cid, options = {}) {
const pathname = options.pathname || ''
try {
// Get onlyIfCached hosts provided
/** @type {string[]} */
Expand All @@ -181,7 +188,7 @@ async function getFromDotstorage (request, cid, { pathname = '' } = {}) {
const headers = getHeaders(request)
headers.set('Cache-Control', 'only-if-cached')

const proxiedResponse = await pAny(
const proxiedCDNResponse = await pAny(
hosts.map(async (host) => {
const response = await fetch(`https://${cid}.ipfs.${host}${pathname}`, {
headers
Expand All @@ -198,7 +205,7 @@ async function getFromDotstorage (request, cid, { pathname = '' } = {}) {
})
)

return proxiedResponse
return proxiedCDNResponse
} catch (_) {}
return undefined
}
Expand All @@ -210,11 +217,12 @@ async function getFromDotstorage (request, cid, { pathname = '' } = {}) {
* @param {Headers} headers
* @param {Env} env
* @param {import('./index').Ctx} ctx
* @return {Promise<ProxiedLayeredResponse>}
*/
async function getFromGatewayRacer (cid, pathname, headers, env, ctx) {
const winnerUrlPromise = pDefer()
let response
let winnerResponseFetched = false
let layerOneIsWinner = false

try {
// Trigger first tier resolution
Expand All @@ -226,7 +234,7 @@ async function getFromGatewayRacer (cid, pathname, headers, env, ctx) {
gatewaySignals,
onRaceEnd: async (gatewayResponsePromises, winnerGwResponse) => {
if (winnerGwResponse) {
winnerResponseFetched = true
layerOneIsWinner = true
winnerUrlPromise.resolve(winnerGwResponse.url)
ctx.waitUntil(
reportRaceResults(env, gatewayResponsePromises, winnerGwResponse.url, gatewayControllers)
Expand All @@ -238,7 +246,7 @@ async function getFromGatewayRacer (cid, pathname, headers, env, ctx) {
}
}
})
if (!winnerResponseFetched) {
if (!layerOneIsWinner) {
throw new Error('no winner in the first race')
}
} catch (err) {
Expand All @@ -263,7 +271,8 @@ async function getFromGatewayRacer (cid, pathname, headers, env, ctx) {

return {
response,
url
url,
resolutionLayer: layerOneIsWinner ? RESOLUTION_LAYERS.PUBLIC_RACE_L1 : RESOLUTION_LAYERS.PUBLIC_RACE_L2
}
}

Expand All @@ -272,7 +281,7 @@ async function getFromGatewayRacer (cid, pathname, headers, env, ctx) {
*
* @param {Request} request
* @param {Cache} cache
* @return {Promise<ProxiedResponse | undefined>}
* @return {Promise<ProxiedCDNResponse | undefined>}
*/
async function getFromCacheZone (request, cache) {
const response = await cache.match(request)
Expand All @@ -292,7 +301,7 @@ async function getFromCacheZone (request, cache) {
*
* @param {Request} request
* @param {Env} env
* @return {Promise<ProxiedResponse | undefined>}
* @return {Promise<ProxiedCDNResponse | undefined>}
*/
async function getFromPermaCache (request, env) {
const response = await env.API.fetch(
Expand Down
2 changes: 2 additions & 0 deletions packages/edge-gateway/test/analytics.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ test('Gets content from first tier gateway race', async (t) => {

// Validate x-dotstorage headers
t.assert(response.headers.get('x-dotstorage-resolution-id'))
t.is(response.headers.get('x-dotstorage-resolution-layer'), 'public-race-l1')

const bindings = await mf.getBindings()

Expand Down Expand Up @@ -100,6 +101,7 @@ test('Gets content from second tier gateway race', async (t) => {

// Validate x-dotstorage headers
t.assert(response.headers.get('x-dotstorage-resolution-id'))
t.is(response.headers.get('x-dotstorage-resolution-layer'), 'public-race-l2')

const bindings = await mf.getBindings()

Expand Down
11 changes: 4 additions & 7 deletions packages/edge-gateway/test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { test, getMiniflare } from './utils/setup.js'
import { addFixtures } from './utils/fixtures.js'
import { GenericContainer, Wait } from 'testcontainers'

import { RESOLUTION_LAYERS } from '../src/constants.js'
import { createErrorHtmlContent } from '../src/errors.js'

test.before(async (t) => {
Expand Down Expand Up @@ -62,9 +61,8 @@ test('Gets content', async (t) => {
t.is(response.headers.get('content-length'), '23')

// Validate x-dotstorage headers
t.is(
response.headers.get('x-dotstorage-resolution-layer'),
RESOLUTION_LAYERS.PUBLIC_RACE
t.assert(
response.headers.get('x-dotstorage-resolution-layer')
)
t.assert(response.headers.get('x-dotstorage-resolution-id'))
})
Expand All @@ -81,9 +79,8 @@ test('Gets content with path', async (t) => {
t.is(response.headers.get('content-length'), '35')
t.is(response.headers.get('content-type'), 'text/plain; charset=utf-8')
// Validate x-dotstorage headers
t.is(
response.headers.get('x-dotstorage-resolution-layer'),
RESOLUTION_LAYERS.PUBLIC_RACE
t.assert(
response.headers.get('x-dotstorage-resolution-layer')
)
t.assert(response.headers.get('x-dotstorage-resolution-id'))
})
Expand Down

0 comments on commit 08f3a6d

Please sign in to comment.