backendgym backendgym · projects Sign in with GitHubSign in

projects /14-short-links-two-million

# Short links, for two million customers

After the merger, two million customers open their own links at five hundred a second, over a year of 22 million links. Maya wants openings by the hour, and someone is trying codes all night.

Advanced ~12h any stack 3 milestones

series The URL shortener · step 4 of 4

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

practises rate-limiting, indexes, query-planning

short-links-two-million / spec.md v1.0

Starts from the repository you shipped in step 3, Short links, one per customer.

After the merger

A year after the first campaign with a link per customer, Pantry bought Southmart, and the customer list went from 400,000 to two million. From now on every Thursday campaign is two million SMS, each with its own link. In the first minutes about five hundred customers a second open theirs, and almost every one of them is a different link.

That year of Thursdays is in the store: 51 campaigns of 400,000 links, most of them opened once or twice in the two days after their SMS. With this week's, that is 22 million links, and nobody deletes any.

Maya now wants to know when people open: each campaign's openings in every clock hour it was opened in, for this week's and for last year's, so she can pick the hour to send.

The night of 14 May

On 14 May somebody ran a script against the service all night, trying codes at random: 1.3 million requests, every one answered "not found". Sam worked out that with 22 million links and enough nights, some would not be. He wants an IP address that keeps asking for codes that don't exist turned away before it gets lucky, without turning away everybody else.

Where it is measured

You measure on your machine, with the service set up as it runs in production: the year in your store, three copies, five hundred openings a second. Load the year however is fastest, with each weekly link opened 0 to 3 times at hours in the two days after its campaign, and this week's links not opened yet: the campaign tool is not what this step is about. On your public address the limit and the earlier steps are checked, not the load.

Not the job

No accounts for other shops, no deleting old links, no counting the same person across campaigns, and nothing smarter than the limit against scripts.

How the redirect finds a link among 22 million, where the hourly numbers come from and how the copies share the limit 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 · A year in the store

You know what your step 3 service does with 22 million links and five hundred different ones opened a second.

  • D1 R1 , not recorded yet

    Load the year as the brief describes: 51 weekly campaigns of 400,000 links and this week's 2,000,000. With three copies of your step 3 service, open 500 of this week's links a second, each a different one, for 5 minutes. Then time one read of the campaign's numbers.

    expectYou have the baseline: the p99, the failed requests and how long the read took, with 22 million links in the store. Nothing sets a target for it.

M2 · Two million at noon

This week's campaign opens at five hundred a second, and Maya reads every campaign by the hour.

  1. R1
    With a year in the store (51 campaigns of 400,000 links and this week's of 2,000,000) and three copies set up as in production on your machine, 500 openings a second, each of a different link of this week's campaign, for five minutes, answer without a failed request and without the wait D1 shows.
  2. R2
    For any campaign, the team reads its number of links, how many were opened at least once, its openings, and its openings in each clock hour (UTC is fine) in which it was opened, while Maya waits, with a year in the store. Every number is exact and at most a minute behind the clicks.
  • D2 R1 , not recorded yet

    After your change, run D1's load again on 150,000 of this week's links that D1 did not open: 500 a second for 5 minutes, through the load balancer in front of the three copies, without following redirects.

    expectYour tool sent all 150,000 requests and every one redirected. Put the p99 next to D1's, say what an opening costs the store now, and what gave way first on your machine.

  • D3 R2 , not recorded yet

    Read this week's campaign's numbers just before D2 and two minutes after it. Then read the oldest campaign's. Time each read.

    expectOpened at least once and openings both went up by exactly the redirects D2 received, the hours D2 ran in went up by the same sum, and the oldest campaign's hours add up to its openings. Say how long each read took and what it touched.

M3 · Guessers turned away, live

An IP address trying codes is refused before it gets lucky, everyone else still gets in, and the public service keeps every earlier promise.

  1. R3
    An IP address that asks for more than 100 codes that do not exist within a minute is refused from its 101st: from then until a minute later, everything it asks for answers 429, real links included, whichever copy answers.
  2. R4
    Nobody else is turned away: while one IP address is refused, customers on other IP addresses open their links as before.
  3. R5
    What steps 1 to 3 promised still holds: no code belongs to two links, a retry makes no second link, a disabled link answers 410 within ten seconds, only the team creates links, reads numbers and disables links, and the service runs at its public HTTPS address.
  • D4 R3 , not recorded yet

    From one IP address, through the load balancer in front of the copies, request random codes shaped like yours at 50 a second for 20 seconds, making every tenth request a real code of this week's campaign. Keep each response's time and status.

    expectUntil the first 429, the real codes redirect and at most 100 requests answer 404. From the first 429 on, every request answers 429, the real codes too.

  • D5 R4 , not recorded yet

    During D4 or in the 30 seconds after it, open 100 of this week's links from another IP address (another device, or a container your load balancer sees as another client). Then, 70 seconds after D4's last request, open a real link from D4's IP address.

    expectAll 100 redirect, and so does the late request from D4's IP address.

  • D6 R5 , not recorded yet

    On your machine: play the campaign tool for 10,000 new destination addresses and count shared codes; send 100 new ones to all three copies at once and count those that got two links; disable a link and open it ten seconds later.

    expectNo shared code, no destination with two links, and the disabled link answers 410.

  • D7 R5 , not recorded yet

    On your public address, run D4 for 20 seconds, with links you created there as the real codes. Then, on a phone on mobile data, open a link from step 1 and one from step 3, and try to read a campaign's numbers without the team's credential.

    expectAt most 100 of D4's requests answered 404 and nothing redirected after the first 429; both links land on their address over HTTPS; the read without the credential is refused.

Questions for the stakeholder

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

start the project to ask

Technical guidance

  • ▸Load the year before anything else and keep the loader in your repository. Some problems only exist at 22 million rows.
  • ▸Run D1 before changing anything, and while it runs, look at what each copy and the store are doing.
  • ▸Keep the list of this week's codes the loader wrote: D1, D2 and D4 read it.

Done when

Every drill above passes, and:

  • The README says how to load the year, run three copies, and run each load test on your machine 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.