backendgym backendgym · projects Sign in with GitHubSign in

projects /13-short-links-one-per-customer

# Short links, one per customer

Every customer gets their own short link with their own coupon: 400,000 links made in half an hour by three copies of your service at once. No code may belong to two customers, and none may be guessed from another.

Intermediate ~8h any stack 3 milestones

series The URL shortener · step 3 of 4

Builds on step 2: it starts from the repository you ship there.

practises unique-ids, idempotency, abuse-cases

short-links-one-per-customer / spec.md v1.0

Starts from the repository you shipped in step 2, Short links, at campaign speed.

How many people opened it

In January Maya showed the Christmas numbers: 610,000 openings. Her manager asked how many customers that was, and nobody could say. It could have been 305,000 people opening it twice or 61,000 opening it ten times.

So from now on every SMS carries its customer's own short link, and every link goes to the offers page with that customer's coupon for that campaign in the address, so no address comes up in two campaigns. Before each campaign, Pantry's campaign tool asks your service for 400,000 links, one per customer, in the half hour before the SMS goes out. Each request says which campaign the link is for. The tool runs 100 workers, each waiting for its answer before sending the next, and a request that fails or takes more than ten seconds is tried again, up to three tries in all.

Since a crash in February, Sam from operations runs your service as three copies behind one address, so one of them can go down without the links going with it. All three take requests from the campaign tool at the same time.

What has to hold

A code that belongs to two customers sends one of them to the other's coupon. A code a customer can change by one letter to land on somebody else's link is the same leak, found on purpose. And the campaign tool retries a request that fails or takes too long, so the same address may arrive twice, on two copies at once.

Maya wants one number per campaign: how many of its links were opened at least once.

Three copies and 400,000 links are measured on your machine, with the service set up as it runs in production. Your public deploy runs as many copies as your host gives you, which on a free host may be one.

Not the job

No expiry, no history of when each customer clicked, no limit on how fast someone can try codes, and no new target for the redirect under load.

How codes are made, what the campaign tool's requests look like and how a campaign is read are yours to choose. Write the choices down in DESIGN before you build.

The work, in 3 milestones

Each milestone says what must be true and the drill that proves it. You close a milestone when its drills pass.

M1 · Three copies, one campaign

You know what three copies of your step 2 service do with a campaign's 400,000 links.

  • D1 R1 , not recorded yet

    Before changing anything, run three copies of your step 2 service behind one address, as in production, and play the campaign tool: links for https://pantry.test/offers?c=<n>&coupon=xmas-<n>, n from 1 to 400,000. Keep each code with its address. Note the time, failures and shared codes.

    expectYou have the baseline: time, failures and shared codes for the service you shipped in step 2. Nothing sets a target for it. If it stalled, write down how far it got and what it was waiting for.

M2 · A code for every customer

A campaign's links are made in half an hour, no code belongs to two customers, none can be guessed from another, and a retry makes nothing new.

  1. R1
    With the service running as three copies behind one address on your machine, set up as it runs in production, the campaign tool creates 400,000 links for 400,000 different addresses in the half hour before the SMS goes out, and no request fails after the tool's retries.
  2. R2
    No code ever belongs to two links, however many copies create links at the same time. Every code is at most eight letters and digits and opens exactly the address it was made for.
  3. R3
    Changing one character of a code a customer holds does not open anyone else's link.
  4. R4
    Asking for an address that already has a link gives back that link, even when the request reaches every copy at the same moment.
  • D2 R1 , not recorded yet

    After your change, run D1 again with 400,000 addresses no link has yet (a new coupon prefix and campaign), with the same 100 workers and retries, against the same three copies.

    expectNo request failed after its retries. Say how long it took next to the tool's half hour, and what limited the rate on your machine.

  • D3 R2 , not recorded yet

    From D2's record, count the codes that came back for more than one address, and check that every code is at most eight letters and digits. Then open 2,000 of them at random through the address in front of the copies, without following redirects, and compare each Location with its address.

    expectNo code came back for two addresses, every code has the right shape, and all 2,000 open exactly their own address.

  • D4 R4 , not recorded yet

    Take 1,000 addresses no link has yet, in a campaign of their own. For each, send the same create request to all three copies directly at the same moment, 20 addresses at a time. Then read that campaign's numbers.

    expectAll three copies gave each address the same code, and the campaign has exactly 1,000 links.

  • D5 R3 , not recorded yet

    Pick 200 codes from D2's record at random. For each, request every code that differs from it in exactly one position, trying there each other character your codes can use, without following redirects. Count the ones that redirect.

    expectNone of them redirects. Work out first how many hits pure chance would give with your number of links and possible codes: if that is not far below one, your codes are too easy to guess.

M3 · Maya's number, live

The team reads how many customers opened a campaign, and the public service still does everything steps 1 and 2 promised.

  1. R5
    The team can read, for one campaign, how many links it has, how many were opened at least once and how many openings there were, while Maya waits, with 400,000 links in the campaign.
  2. R6
    What steps 1 and 2 promised still holds with three copies: links made before this step redirect, every redirect is counted, a disabled link answers 410 within ten seconds, only the team creates links, reads counts and disables, and the service runs at its public HTTPS address.
  • D6 R5 , not recorded yet

    A minute after D3, read the campaign's numbers. Through the address in front of the copies, open 10,000 of its links D3 did not open, and 1,000 of those again, 50 at a time, without following redirects. A minute later, read the numbers again and time the request.

    expectThe campaign still has 400,000 links, opened at least once went up by exactly 10,000 and openings by exactly 11,000. Say how long the read took and what it had to touch.

  • D7 R6 , not recorded yet

    Through the address in front of the three copies, open another of the campaign's links 60 times, then disable it. From ten seconds after the disable, open it 60 more times, and read its count a minute later.

    expectThe first 60 redirect, all 60 after the ten seconds answer 410, whichever copy took them, and the count is exactly 60.

  • D8 R6 , not recorded yet

    On your public address, play the campaign tool for 1,000 new addresses in a campaign of their own, and count codes shared by two addresses. On a phone on mobile data, open one of them and a link from step 1. Then, without the team's credential and with a wrong one, try to create a link.

    expect1,000 links with 1,000 different codes, both links land on their address over HTTPS, and both attempts without the credential are refused.

Questions for the stakeholder

They reveal no requirement, but the answers shape the design.

start the project to ask

Technical guidance

  • ▸Run D1 before changing anything. Three copies and 400,000 links may show you something one copy and twenty links never did.
  • ▸Keep the tool that plays the campaign tool in your repository, with its settings in the README: D2 and D8 run it again.
  • ▸Record every code the campaign tool received. D3 and D5 read that list.

Done when

Every drill above passes, and:

  • The README says how to run three copies on your machine, and how to run the campaign tool against them and against the public address.
  • No password, key or connection string is in the repository.
Solutions

No solutions published yet. Ship the project and yours will be the first.