How khalidelokiely/squash-it works
Purpose and Startup
Squash It is a Go URL-shortening service: it creates short hash tokens for long URLs, resolves tokens to destinations, and redirects visit requests while tracking clicks. main.go is the process entry point; dependency construction is centralized in internal/app/provide.go. Runtime configuration is loaded from internal/config/config.go.
HTTP Edge and Routing
A custom router wraps net/http, providing route groups, handler adaptation, options, and serving behavior. Application routes connect HTTP endpoints to handlers; middleware applies cross-cutting controls such as client-based throttling.
• internal/router/router.go
• internal/router/serve.go
• internal/app/routes.go
• internal/app/middleware.go
• internal/app/handlers.go
Application Layer
The app layer owns request/response models and orchestrates encode, decode, and redirect use cases. Handlers parse HTTP DTOs and invoke the service; the service coordinates hashing, Bloom checks, repository persistence, caches, and redirect/click behavior without embedding transport concerns.
• internal/app/model.go
• internal/app/service.go
• internal/app/handlers.go
Persistence and URL Identity
SQLite is the source of truth for URL mappings and click counts. The repository abstracts database operations, including lookup and idempotent find-or-create behavior needed when concurrent or repeated encode requests target an existing long URL. Short tokens are generated with seeded Murmur hashing and retried on collisions.
• internal/db/db.go
• internal/app/repository.go
• internal/hash/hash.go
Encode Control Flow
/encode is rate-limited, then the service generates an 8-character candidate token. A Bloom negative permits an idempotent repository insert; a “maybe present” result triggers DB verification. Matching long URLs reuse their mapping; token collisions retry with another hash seed until success or retry exhaustion. Successful mappings update the Bloom filter.
• internal/app/service.go
• internal/filter/filter.go
• internal/filter/bloom.go
• internal/rate/user_token_bucket.go
Decode and Redirect Read Path
Decode first consults the Bloom filter to reject definite misses cheaply. Possible matches follow an L1/L2 cache pipeline, then SQLite on cache miss. DB hits repopulate cache and Bloom. The visit route follows this resolution path, increments the persisted click count, and returns a 302 Found redirect.
• internal/app/service.go
• internal/cache/pipeline.go
• internal/cache/lru.go
• internal/app/repository.go
State Acceleration and Runtime Infrastructure
The cache pipeline uses in-process LRU as L1 and optional Redis as L2; Redis failure degrades safely to local caching. The Bloom filter is persisted/reconstructed asynchronously and backed up on an interval, guarding DB/cache traffic but allowing false positives. Docker Compose provisions Redis alongside the service; SQLite requires no network dependency.
• internal/cache/redis.go
• internal/cache/provide.go
• internal/filter/persistent_bloom.go
• docker-compose.yml
• Dockerfile
Main entry points: service.go URL service · routes.go Application routes · provide.go Dependency wiring · main.go Process entry
Components 16
HTTP Edge
- Custom router · Wraps net/http route grouping and serving.
internal/router/router.go - Application routes
internal/app/routes.go - Client throttling · Rate-limits encode requests.
internal/app/middleware.go - HTTP handlers
internal/app/handlers.go
Application
- Dependency wiring
internal/app/provide.go - URL service · Orchestrates encode, decode, and redirect behavior.
internal/app/service.go - Token hashing · Generates seeded candidates and supports collision retries.
internal/hash/hash.go
State & Acceleration
- URL repository · Provides lookup, idempotent mapping creation, and click updates.
internal/app/repository.go - SQLite · Stores URL mappings and click counts.
internal/db/db.go - Persistent Bloom filter · Rejects definite misses and is asynchronously persisted/backed up.
internal/filter/persistent_bloom.go - L1/L2 cache pipeline · Uses local LRU and optional Redis with safe degradation.
internal/cache/pipeline.go - Redis L2
internal/cache/redis.go
Runtime
- Process entry
main.go - Runtime config
internal/config/config.go - Docker Compose · Provisions the service with Redis.
docker-compose.yml - Service image
Dockerfile
Connections 16
- Process entry → Runtime config: loads
- Process entry → Dependency wiring: starts
- Dependency wiring → Custom router: assembles
- Dependency wiring → URL service: injects dependencies
- Custom router → Application routes: serves
- Application routes → Client throttling: protects /encode
- Application routes → HTTP handlers: dispatches
- HTTP handlers → URL service: invokes use cases
- URL service → Token hashing: generates candidates
- URL service → Persistent Bloom filter: checks and updates
- URL service → L1/L2 cache pipeline: reads and repopulates
- URL service → URL repository: finds, creates, counts clicks
- URL repository → SQLite: persists mappings and clicks
- L1/L2 cache pipeline → Redis L2: optional L2
- Docker Compose → Redis L2: provisions
- Service image → Process entry: packages service