-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #90 from edgio-docs/ef-caching
Add EF waiting room example
- Loading branch information
Showing
15 changed files
with
643 additions
and
232 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,5 @@ | |
node_modules/ | ||
**/node_modules/ | ||
**/.edgio/** | ||
**/.yalc/** | ||
yalc.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 0 additions & 132 deletions
132
examples/v7-edge-functions/functions/database/upstash/index.js
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import createFetchForOrigin from '../../utils/createFetchForOrigin'; | ||
import '../../utils/polyfills/URL'; | ||
|
||
const fetch = createFetchForOrigin('echo'); | ||
|
||
export async function handleHttpRequest(request, context) { | ||
const { method } = request; | ||
const body = method === 'POST' ? await request.arrayBuffer() : null; | ||
|
||
// get the headers from the incoming request, removing the content-length header | ||
const headers = Object.fromEntries( | ||
[...request.headers.entries()].filter(([key]) => key !== 'content-length') | ||
); | ||
|
||
const newRequest = new Request('https://http-echo.raees.me', { | ||
method, | ||
headers, | ||
body, | ||
}); | ||
|
||
const response = await fetch(newRequest); | ||
|
||
// apply caching headers to the response for all HTTP methods | ||
response.headers.set('cache-control', 's-maxage=600'); | ||
|
||
return response; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
examples/v7-edge-functions/functions/waiting-room/constants.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
export const COOKIE_NAME_ID = '__edgio_session_id'; | ||
export const COOKIE_NAME_TIME = '__edgio_session_last_update_time'; | ||
export const TOTAL_ACTIVE_SESSIONS = 2; | ||
|
||
// Active sessions should persist longer than queued sessions | ||
export const ACTIVE_SESSION_DURATION_SECONDS = 150; | ||
export const QUEUED_SESSION_DURATION_SECONDS = 15; | ||
|
||
// Queries | ||
export const CREATE_SESSION_QUERY = ` | ||
-- Conditional insert if the session does not exist. | ||
-- This is cheaper than a SELECT followed by an INSERT. | ||
INSERT INTO sessions (sessionId, status, createdAt, updatedAt) | ||
SELECT | ||
:id, | ||
IF((SELECT COUNT(*) FROM sessions WHERE status = 'active') >= :sessionsLimit, 'queued', 'active'), | ||
NOW(), | ||
NOW() | ||
FROM dual | ||
WHERE NOT EXISTS ( | ||
SELECT 1 FROM sessions WHERE sessionId = :id | ||
); | ||
`; | ||
|
||
export const GET_SESSION_QUERY = ` | ||
-- Get the session data and the position in the queue. | ||
SELECT | ||
sessions_all.*, | ||
IF(sessions_all.status = 'queued', queued_info.position, -1) AS position, | ||
(SELECT COUNT(*) FROM sessions WHERE status = 'active') AS active_count, | ||
(SELECT COUNT(*) FROM sessions WHERE status = 'queued') AS queued_count | ||
FROM | ||
(SELECT | ||
sessionId, | ||
status, | ||
createdAt, | ||
updatedAt | ||
FROM sessions) AS sessions_all | ||
LEFT JOIN | ||
(SELECT | ||
sessionId, | ||
ROW_NUMBER() OVER (ORDER BY createdAt) AS position | ||
FROM sessions | ||
WHERE status = 'queued') AS queued_info | ||
ON sessions_all.sessionId = queued_info.sessionId | ||
WHERE sessions_all.sessionId = :id; | ||
`; | ||
|
||
export const EXPIRE_SESSIONS_QUERY = ` | ||
DELETE FROM sessions WHERE | ||
(updatedAt < DATE_SUB(NOW(), INTERVAL :activeDuration SECOND) AND status = 'active') | ||
OR (updatedAt < DATE_SUB(NOW(), INTERVAL :queuedDuration SECOND) AND status = 'queued'); | ||
`; | ||
|
||
export const GET_SESSION_ID_QUERY = ` | ||
SELECT sessionId FROM ( | ||
SELECT sessionId FROM sessions WHERE sessionId = :id | ||
UNION ALL | ||
SELECT UUID() AS sessionId | ||
ORDER BY (sessionId IS NOT NULL) DESC | ||
LIMIT 1 | ||
) AS uuid_selection; | ||
`; | ||
|
||
export const REFRESH_SESSION_QUERY = ` | ||
UPDATE sessions | ||
SET | ||
updatedAt = :date, | ||
status = :status | ||
WHERE sessionId = :id; | ||
`; | ||
|
||
export const AVAILABLE_STATUS_QUERY = ` | ||
SELECT IF(COUNT(*) < :sessionsLimit, 'active', 'queued') as newStatus | ||
FROM sessions | ||
WHERE status = 'active'; | ||
`; |
78 changes: 78 additions & 0 deletions
78
examples/v7-edge-functions/functions/waiting-room/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { | ||
getCookiesFromRequest, | ||
setCookieToResponse, | ||
} from '../../utils/cookies'; | ||
import { setEnvFromContext } from '../../utils/polyfills/process.env'; | ||
import waitingPage from './waiting-room-capacity.html'; | ||
import landingPage from './waiting-room-landing.html'; | ||
import { COOKIE_NAME_ID, COOKIE_NAME_TIME } from './constants'; | ||
import { getSessionData } from './planetscale'; | ||
|
||
/** | ||
* Main handler for the edge request. | ||
*/ | ||
export async function handleHttpRequest(request, context) { | ||
let response; | ||
|
||
// Set context environment variables to process.env | ||
setEnvFromContext(context); | ||
|
||
const cookies = getCookiesFromRequest(request); | ||
|
||
// Get user ID from cookie or generate a new one | ||
const sessionId = cookies[COOKIE_NAME_ID]; | ||
|
||
// Get the current number of active sessions and the active user | ||
const { session, activeCount, queuedCount } = await getSessionData(sessionId); | ||
|
||
// Check capacity | ||
if (session.status === 'active') { | ||
response = await getDefaultResponse(request, session); | ||
} else { | ||
response = await getWaitingRoomResponse(request, session); | ||
} | ||
|
||
// Update the session cookie with the latest timestamp | ||
setSessionCookie(response, session.sessionId, session.updatedAt); | ||
|
||
return response; | ||
} | ||
|
||
/** | ||
* Handle the default response. | ||
*/ | ||
async function getDefaultResponse(request, session) { | ||
const response = new Response(landingPage({ requestUrl: request.url })); | ||
response.headers.set('content-type', 'text/html;charset=UTF-8'); | ||
|
||
return response; | ||
} | ||
/** | ||
* Response for the waiting room. | ||
*/ | ||
async function getWaitingRoomResponse(request, session) { | ||
// update the waiting page to show the position in the queue, replacing {{queuePosition}} | ||
const body = waitingPage({ | ||
queuePosition: session.position, | ||
}); | ||
|
||
const response = new Response(body); | ||
response.headers.set('content-type', 'text/html;charset=UTF-8'); | ||
|
||
return response; | ||
} | ||
|
||
/** | ||
* Sets the session cookie to the response. | ||
* | ||
* @param {Response} response | ||
* @param {Object} session | ||
* @param {number} date | ||
* @returns {Promise<void>} | ||
*/ | ||
export async function setSessionCookie(response, sessionId, date) { | ||
const now = date || Date.now(); | ||
|
||
setCookieToResponse(response, [[COOKIE_NAME_TIME, now.toString()]]); | ||
setCookieToResponse(response, [[COOKIE_NAME_ID, sessionId]]); | ||
} |
Oops, something went wrong.