DHH vừa đặt ra thuật ngữ "Citadel", cuối cùng cho chúng ta một cách tuyệt vời để reference cách chúng ta approach tech tại AppSignal. Chúng tôi nói, "Hey, this is us! Thing của chúng tôi có tên rồi".
In addition to the Majestic Monolith, someone should write up the pattern of The Citadel: A single Majestic Monolith captures the majority mass of the app, with a few auxiliary outpost apps for highly specialized and divergent needs.
— DHH (@dhh) April 7, 2020
Để explain cách AppSignal dùng Citadel pattern, chia sẻ chút về hệ thống của chúng tôi. AppSignal là monitoring product có user-facing application và API mà monitoring agent gửi data đến. Data này sau đó processed và biến thành graphs và insights.
Monolith
Application khách hàng interact với là monolithic Rails app, với parts của front-end viết bằng React. Backend hoàn toàn viết bằng Ruby và talk to vài databases (chúng tách data từ different customers ra separate clusters cho scaling reasons). Rails app này cũng handle bunch other tasks như sending alerts đến external services.
Khi bắt đầu, Rails app này cũng processed incoming data từ monitoring agent, chúng tôi foresaw data ingestion sẽ trở thành bottleneck. Vì vậy dùng Sinatra app chạy trên subdomain ingest data và tạo Sidekiq jobs processed bởi Rails app.
Growing Pains
Kiến trúc này work well nhiều năm. Khi business grow, rõ ràng task cụ thể process incoming data từ agents sẽ cần special treatment. Khi monitor billions và billions requests, bạn run into hard limits. Main limiting factor không phải Ruby chậm (chúng ta biết không 😉), mà cách chúng ta architected things caused too much locking trong databases.
Một Outpost
Chúng tôi looked at several possibilities và decided Kafka là best fit cho situation. Có experience với Rust và nghĩ speed và reliability của nó very good fit cho system này. Chúng tôi rewrote data ingestion và processing system in Rust, dùng Kafka as combination của queue và storage system.
Chỉ moved incoming data processing part của Rails app sang outpost service này. Phần còn lại của systems work well in form của monolithic app. Chúng tôi understand it deeply, và like keeping things simple. Monolith vẫn handle most logic và interact với Kafka heavily. Khát vọng keep monolith led đến viết Kafka gem, để main app communicate với outpost easily.
Nếu muốn learn more về Kafka tại AppSignal check out Railsconf talk tôi gave về này.
Cuộc Sống Tại Citadel
Điều này bring us đến current situation nơi chúng tôi very happy in our citadel. Như DHH nói:
A single Majestic Monolith captures the majority mass of the app with a few auxiliary outpost apps for highly specialized and divergent needs.
Trong case chúng tôi, có single outpost service cho highly specialized needs. Nếu có RailsConf năm nay, chúng tôi would have given DHH some extra stroopwafels as appreciation cho đặt tên nó. 🍪