Challenges / The token that ran out
The token that ran out
Quotes API asks the Carrier for a new access token for every parcel, and the Carrier turns most of them away. Get every parcel a quote.
The setup
Quotes API prices the shipping for each parcel it is sent. The rates are the Carrier's, a company outside your system, and it wants an access token with every rate it is asked for.
The Carrier's code is not yours to read. Select it and open its Config tab to see what it takes, what it answers, and what it limits.
450 parcels arrive, ten a second and then twenty. Every one of them needs a quote.
The run
Each goal is judged over its own stretch of the run rather than the whole of it.
- 5 s · The morning's orders arrive, twenty parcels a second
- No token request is turned away0 s–end
- Every parcel is answered without an error0 s–end
- All 450 parcels have a quote from the Carrier0 s–end
The code
Ordinary Node against the real AWS SDK, running as real processes in the tab. You can edit any of it once the challenge is open.
import express from 'express';
// Glass Garden gives every instance its own PORT, because they all share one machine here.
// In a real deployment they would each be on their own machine, and could all listen on the same port.
const port = Number(process.env.PORT);
if (!port) {
throw new Error('PORT is not set');
}
// Named for the node it comes from, so connecting something else here would not change it.
// The Config tab lists every variable this instance is launched with
const carrier = process.env.CARRIER_URL;
if (!carrier) {
throw new Error('CARRIER_URL is not set. Connect this instance group to Carrier.');
}
// The Carrier answers a rate request only when it carries an access token
async function accessToken() {
const response = await fetch(`${carrier}/token`, { method: 'POST' });
if (!response.ok) {
throw new Error(`The Carrier answered ${response.status} to a token request`);
}
const { access_token } = await response.json();
return access_token;
}
const app = express();
app.use(express.json());
app.post('/quotes', async (req, res) => {
const { parcel } = req.body;
const token = await accessToken();
const response = await fetch(`${carrier}/rates?parcel=${parcel}`, {
headers: { authorization: `Bearer ${token}` }
});
if (!response.ok) {
throw new Error(`The Carrier answered ${response.status} for parcel ${parcel}`);
}
const { rate } = await response.json();
res.json({ parcel, shipping: rate });
});
// Express hands a rejected handler here. A parcel that could not be priced is this server
// failing its caller, whoever was at fault behind it
app.use((error, req, res, next) => {
console.error(error.message);
res.status(502).type('text/plain').send(`${error.message}\n`);
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});More challenges
Your first challenge
Nothing is sending the app traffic, and its code fails on every request. Wire it up, fix one line, and watch a run score itself.
Instance Group
The concert that oversold
Box Office counts the tickets it has left. Keep the concert from selling more than it has when Box Office scales out.
HTTP Load Balancer, Instance Group, Table (DynamoDB)
Signups grind to a halt
The signup API hashes every password and stores it while the caller waits. Keep signups answering when the traffic climbs.
HTTP Load Balancer, Instance Group, Table (DynamoDB)
Usually solved with: Queue (SQS), Function (Lambda)