Jongin Kim
한국어Backend and systems software engineer · Portfolio
- Education
- B.S. in Computer Science, KAIST (expected February 2027)
- Experience
- Lucentblock, Dec 2018 – Dec 2023 · Backend for a financial platform, including about two years (2020 – 2021) building a matching engine in Rust
Sahoipyoungnon, Jan – Mar 2026 · Freelance backend developer (Java/Spring, rebuilding a learning platform) - Languages
- The work below was done in Rust, TypeScript, Java, JavaScript (Node.js) and Python. I used C in university coursework.
Case 1
Choosing the orderbook data structure: no reallocation at runtime
Order price
→
Tick index
→
Pre-sized Vec
price slots 0 … N
→
price slots 0 … N
Per-price order queue
first in, first matched
first in, first matched
① Array + order queue chosen
② B+ tree + flyweight
③ Dense hash set
Problem
- A matching engine reads and updates the orderbook on every order, so the data structure directly affects latency.
- Rebuilding the engine in Rust meant choosing the orderbook structure again.
- Reallocation at runtime or scattered memory access can make latency unpredictable.
Solution
- Studied C++ orderbook implementations and compared three options: array + order queue, B+ tree + flyweight, and dense hash set.
- Investigated how a local allocator affects insert, delete, cancel and reduce, and raised memory fragmentation as a team discussion item.
- Because hardware fetches memory in cache lines, a contiguous array was the better fit. Prices map to tick indices, and a pre-sized Rust
Vecremoves reallocation at runtime.
Result
- Wrote tests for the orderbook's public functions and scenario tests, and merged them with the implementation.
- Those tests caught two defects, which I fixed: the best price not updating after an order was removed, and zero-quantity entries leaking into change messages.
Case 2
Designing a benchmark tool to find the engine's limits
Test config
→
Benchmark tool
controls Kubernetes
→
controls Kubernetes
Load workers
→
Kafka
→
Matching engine
CPU and memory limits
CPU and memory limits
↓
Per-stage log collection
→
Automatic verdict
throughput, availability, restarts, errors
→
throughput, availability, restarts, errors
Report and dashboards
Problem
- There was no way to check repeatedly, under the same conditions, how much load the engine could take or what a code change made better or worse.
- Manual measurement changed the data, resources and measured window every time, so results were hard to compare.
Solution
- Designed a tool that runs on Kubernetes. Load tests add workers step by step, stress tests vary the engine's CPU and memory limits, and performance tests pre-load random orders for the engine to process.
- Throughput, availability, restarts and critical errors are judged automatically against predefined criteria, and each run leaves a report linked to dashboards.
- When some latencies came out negative, I checked the measurement data before suspecting the engine. The causes were lost start or end logs for a stage, duplicates from the engine re-processing the same Kafka messages after a restart, and the aggregation query picking the wrong timestamp among several logs. I filtered out samples whose start and end log counts or uniqueness did not match, and changed the query to use the earliest log as the start time. On re-checking, the negative stages I had found were gone.
Result
- Each deployment's changes and results were recorded together, so runs could be compared.
- Compared repeated runs before and after changing the async runtime's channel with a t-test, and reported both the stages that improved and the stage that got worse. The two runs had different log-loss rates, so I did not draw a throughput conclusion from that data.
- Later the same method caught a performance regression, and re-running under different resource settings ruled out the environment first.
- Measuring repeatedly under the same conditions with this tool and removing bottlenecks one at a time, I raised throughput from around 100 to about 38,000 per second by the end of the 2021 optimization work.
Case 3
A compliance ledger module and DB-based migration
Service events
→
Message broker
→
Ledger module
Spring Kafka listener
→
Spring Kafka listener
Internal blockchain ledger
↑
Historical data in DB
→
Migration
rebuilds messages
→
rebuilds messages
On failure, stop ordered processing
Problem
- For compliance, key transaction history had to be recorded on an internal blockchain ledger.
- Existing data had to be recorded too, but older events had already passed the broker's retention period.
Solution
- Studied how Spring Kafka listener containers, partition assignment and offset reset work, then built a handler per event type.
- Designed a migration that rebuilds messages from the DB, triggered when partitions are assigned.
- If the migration fails, ordered processing stops, so records never continue out of order.
Result
- Testing surfaced two problems: circuit breaking did not work when partitions were assigned across multiple threads, and the migration could run twice.
- Discussed the migration strategy with the team, documented it, and rolled it out to production.
Case 4
Switching deposit notifications from a process per event to ZMQ subscriptions
Blockchain node daemon
ZMQ publisher
→
ZMQ publisher
Notifier
subscriber on the same host
→
subscriber on the same host
DB
deposits, blocks
→
deposits, blocks
Internal server
New block arrives
→
Compare with stored hashes
→
Find the fork point
unconfirmed blocks only
→
unconfirmed blocks only
Re-sync blocks and deposits
Problem
- Each deposit event launched a new process running a script that sent an HTTP request. As the number of managed addresses grew, spawning processes could use a lot of memory.
- The server had to make many RPC calls to the daemon to exchange information.
- A chain fork could leave the blocks stored in the DB out of sync with the actual chain.
Solution
- Ran a Notifier on the daemon's host that subscribes to transactions and blocks over ZMQ pub/sub, cutting traffic between servers.
- Implemented four features: recording and forwarding deposits, forwarding blocks that reached the confirmation count, recording which block a deposit was mined in, and repairing divergence between the DB and the chain.
- Repair loads only the blocks within the confirmation count from the DB and stops calling RPC once it finds the matching point. I also fixed a hash mismatch caused by the network buffer's little-endian byte order.
Result
- Tested each of the four features against a test daemon and server in Docker, and documented the results.
- Reproduced a chain fork and confirmed that, on the next block, the Notifier found the diverged blocks and re-synced their hashes and deposits.