kunhua.sh

这里那里

LGTMGo · Gin · Next.js · SQLite/Postgres · Redis · SSE · live · code

An assistant that reviews GitHub pull requests, live. It reads CI status and the repository's own convention docs, not just the diff. Repo-level RAG is achieved.

  • Building the context: handing a model only the diff produces comments detached from the project, so a review starts by pulling the PR's metadata, its CI status, and the repository's own convention documents
  • A three-stage pipeline: one long response is hard to act on or reuse, so the review is split into a change summary, a risk list, and fix suggestions. Each stage can use a different model and be retried on its own; the output is structured rather than prose, with suggestions anchored to specific file lines
  • Streaming the result: a full review takes long enough that waiting on a blank page is unpleasant, so a self-built SSE protocol streams the summary as it is written and replaces the risks and suggestions when their stages finish
  • GitHub App integration: OAuth for sign-in, a webhook that reviews new pull requests and pushes automatically, and the results written back as inline suggestions
  • Caching and idempotency: re-triggering the same PR would otherwise mean paying for the same model calls, so results are persisted in SQLite or Postgres behind a Redis cache, and webhook deliveries are made idempotent
  • Internationalization: the interface language and the language the model writes in have to agree, so one self-built locale layer drives both. PR comments stay in English regardless, so a repository's maintainers only ever read one language
  • Deployment: the frontend on Vercel, the Go backend containerized on Fly.io, persistence switchable between SQLite and Postgres.
BetterScrobblerC++17 · Objective-C++ · Go · CGo · macOS MediaRemote · code

A Last.fm scrobbler for macOS that logs playback from any source. Written first in C++/Objective-C++, later rewritten in Go. The now-playing line on this site comes from it.

  • System-wide capture: macOS has no reliable system-wide scrobbler — Apple Music has none built in and the old plugin approach is deprecated — so this reads the operating system's now-playing state directly. Spotify, Apple Music, YouTube and browsers are all captured, with parser rules skipping anything that is not music
  • Calling a private framework: that state is only available from Apple's undocumented MediaRemote framework. The C++ version declares its private interfaces in Objective-C++ and links it through CMake with -F/System/Library/PrivateFrameworks; the Go version bridges through CGo, which means marshalling types on both sides of the C boundary
  • Deciding what counts: not every play should be logged, so the engine splits into stream, track and scrobble managers plus a timer, and the timer applies Last.fm's rule for when a play becomes a scrobble
  • Filling the gaps: sources MediaRemote does not cover are picked up through AppleScript and browser audio detection
  • Request signing: Last.fm requires signed requests, implemented by hand — sort the parameters, append the secret, take the md5
  • Credential storage: authentication details go into the macOS Keychain
  • Terminal interface: a Bubble Tea UI with synchronized LRC lyrics, and a background daemon mode
RPCinGoGo · TCP · etcd · Protobuf · code

A demo-grade RPC framework over TCP, with service discovery, load balancing and connection reuse.

  • Protocol and codecs: a custom binary protocol with a fixed-length header and a variable body, switchable between JSON and Protobuf to keep serialization cheap
  • Multiplexing and pooling: opening a connection per call adds latency jitter under load, so concurrent requests share one TCP connection keyed by request ID — which also means responses returning out of order still route correctly — and idle connections are pooled to remove the handshake from every call
  • Finding the bottleneck: at 32 concurrent clients, throughput went from 25K to 74K QPS and p99 from 6.7ms to 2.0ms. The cost was disk I/O from logging on the hot path, 1.9 million lines in a single run, alongside moving serialization from JSON to Protobuf. Separately, several goroutines writing one connection interleaved frames and corrupted them; a single writer goroutine now serializes outbound frames
  • Failure and rate control: retrying into a failing dependency exhausts resources and cascades, so a sliding-window circuit breaker rejects requests once the error rate crosses a threshold, with token-bucket and sliding-window rate limiters alongside it
  • Discovery and balancing: clients need to see nodes appear and disappear as services scale, so the client watches etcd for the service list and implements several balancing strategies over it
  • Interceptors and observability: interceptor chains on both the client and server sides, carrying Recovery, Logging, Prometheus metrics, Retry, and OpenTelemetry tracing
GorderGo · gRPC · RabbitMQ · MySQL · MongoDB · Redis · code

A demo-grade order system in Go, split across four services, covering the path from placing an order to fulfilling it.

  • Service boundaries and transport: the order flow spans ordering, stock, payment and fulfilment, so it is split into four services that talk over gRPC where a response is needed immediately and over RabbitMQ events where it is not
  • Stock contention and flash sales: overselling is prevented by a reservation table and a compare-and-swap inside a MySQL transaction. The flash-sale path adds a Redis Lua script that validates, enforces one order per person, and decrements atomically at the entrance, so the real stock is written asynchronously after the spike and the database sees less contention
  • Payment timeouts: polling for unpaid orders is expensive and slow to react, so orders expire through a RabbitMQ TTL and dead-letter queue; the order service consumes that event, reverses the order's state and returns the stock
  • Order state: a wrong transition loses money or leaks stock, so transitions are defined as rules and validated inside the service, with refunds and stock returns triggered through events. Orders live in MongoDB, written atomically through a MongoDB session and transaction so a state cannot be skipped
  • Distributed tracing: once several services were deployed, logs were scattered and a full call path could not be reconstructed. OpenTelemetry and Jaeger provide the tracing; trace context does not propagate across asynchronous calls on its own, so it is carried in RabbitMQ message headers
  • Operations: Consul for service registration and health checking, Prometheus and Grafana for endpoint latency and call counts, which is how slow requests get located
EGOS-2000C · RISC-V assembly · QEMU

Kernel subsystems built inside a RISC-V teaching operating system: signals, scheduling, virtual memory, user-level threads.

  • Signal delivery: a signal should take effect the next time its process is scheduled rather than interrupt the kernel, so pending signals are held in a bitmask and delivery hangs off kernel_entry just before the context is restored. Delivering one saves mepc and 32 registers into the process's saved_signal_ctx, rewrites mepc to the handler, and puts the signal number in a0 and the trampoline's address in ra
  • Returning to user space: a handler has to resume where the process was interrupted, and user code cannot restore the context the kernel saved, so a short RISC-V assembly trampoline sits in the user program: the handler returns into it, it writes SYS_SIGRETURN to the syscall argument page and issues ecall, and the kernel restores what it saved
  • Defaults and uncatchable signals: SIGKILL and default handling skip that path entirely, rewriting mepc to the program's exit entry so user code cannot intercept them
  • MLFQ scheduling: CPU-bound processes starve interactive ones, so processes fall through three levels by accumulated CPU time — high within one quantum, middle within two, low after that — with system processes pinned to the high queue and a separate sleep queue
  • Virtual memory: two-level Sv32 page tables, where walk() allocates an L2 page on demand and returns a pointer to the PTE so permission bits can be edited directly, with sfence.vma after a switch and identity-mapped regions set up alongside
  • Memory protection: PMP marks 0x802000000x80400000 as a NAPOT region with r/w/x for user mode, written straight into the pmpaddr0 and pmpcfg0 CSRs
  • User-level threads: TCBs, a FIFO ready queue, and thread_create / thread_yield / thread_exit. A thread cannot free its own stack while running on it, so reclamation is deferred to the next create or yield
FUSE File SystemC · FUSE · Python · code

A Unix-like file system in user space, read and write, with two unit-test suites totalling 39KB.

  • On-disk format: a custom 4096-byte block layout with a magic-checked superblock. An inode fills a whole block — uid, gid, mode, ctime, mtime and size, followed by 1019 direct block pointers — and a directory entry is a fixed 32 bytes holding a 1-bit valid flag, a 31-bit inode number and a 28-byte name
  • Block allocation: free blocks are tracked in a bitmap held in memory and maintained through bit_set, bit_clear and bit_test, updated on creation, write and truncation
  • Path resolution: done in two steps that everything else reuses — parse splits a path into components, translate walks the directories to turn it into an inode number
  • File system operations: 16 FUSE callbacks covering getattr, readdir, create, mkdir, unlink, rmdir, rename, chmod, utime, truncate, read, write and statfs, with both reads and writes complete
  • Truncation and renaming: truncate reclaims or adds data blocks to reach the new length and keeps the bitmap in step; rename handles adding and removing directory entries on both sides of a move
  • Tooling: three Python utilities alongside it — gen-disk.py builds a disk image from a description, read-img.py dumps one, diskfmt.py formats
  • Tests: two Check suites totalling 39KB, against 34KB of implementation