state, which you set to in-memory back in Chapter 1. Restart the engine and
everything is gone. In this chapter you add a database worker (SQLite) that holds the durable
record of links and a timestamped row for every click on a short code. state stays in the picture
as a fast read cache in front of the database.
state can also persist on its own (store_method: file_based with a file_path). This chapter
uses a dedicated database worker instead, which gives you durable storage plus SQL to query it.Add the database worker
State is a fast cache, but you also want a durable record you can run SQL over: every link, and a timestamped row each time someone follows one. Uncomment the Ch. 3 block inworker-compose.yaml,
and uncomment the start_after line under link:
worker-compose.yaml
For simplicity and ease of configuration we’ve pre-included the database configuration and are
instructing you to uncomment entries and restart the entire project. This is not necessary, you
can also:
- Add a worker with
iii trigger compose::add worker=database - Configure it in
worker-compose.yaml - Restart just that worker with
iii trigger compose::restart worker=database.
The database worker supports more than SQLite, refer to the
database worker
docs for all supported databases.link/src/index.ts in pieces.
Define the database
First add theDB constant near the top of link/src/index.ts:
src/index.ts
Make link storage persistent
Now we’re going to adapt the existinglink::create and link::resolve functions so that they
write and read from our new database while using our state worker as a hot cache.
Create a schema
Add anensureSchema() function to link/src/index.ts that creates both tables on startup. The
database worker accepts SQL through its database::execute function. The database worker can
register a moment after link, so add a few retries:
start_after in worker-compose.yaml controls worker start order. In this case we are having the
link worker start_after database. This helps with reliability but it does not guarantee that
the database worker will be ready when the link worker starts.src/index.ts
Setup database writing
Modifylink::create to write to both the database (durable record) and state (hot cache):
src/index.ts
Setup database retrieval
Modifylink::resolve to check the cache first; on a miss, fall back to the database and warm
the cache for the next read. It’s easiest to replace the existing link::resolve function with our
new version:
src/index.ts
Add click tracking
Since we have a database now, you can start click tracking. Make a new function (link::record_click) to do that and save it to the database. The next chapter will move this work
onto a queue so that it can run without touching the redirect’s logic. Add it below link::resolve:
src/index.ts
Update http::redirect to call link::record_click
Now update http::redirect to trigger it directly, right before returning the redirect:
src/index.ts
The database write for clicks adds latency to every redirect. The next chapter moves it onto a
durable queue that removes the latency while also adding recovery from database failures.
Try the click tracking
Now let’s see the click tracking in action. Save the file, create a few links, and simulate a click on each:Conclusion
Did you know that
--help works with function id’s as well? Try running: iii trigger database::query --help to see what arguments database::query accepts.state keeps lookups fast, and
every redirect appends a timestamped row to the clicks table. But that row is written on the
redirect’s hot path, so a slow database write slows the redirect. Next, in
Ch. 4: Make it durable, you move that write onto a queue so
redirects stay fast.