#261 - Một tuần làm việc của Engineering Manager
Nhóm biên tập có nhận được nội dung chia sẻ từ bạn đọc T đang làm việc ở một công ty công nghệ với quy mô trên 1000 nhân sự với vai trò Engineering Manager. Nhóm sẽ bắt đầu trích đăng nội dung chia sẻ của bạn viết về công việc hàng ngày của mình trong các số newsletter tới.
Ngoài ra, để nội dung thêm phong phú nhóm cũng hy vọng có dịp phỏng vấn các bạn đang làm việc ở các công ty công nghệ với các vai trò khác nhau như Software Engineer, QA, Manager, Product Owner, ML Ops, DevOps… chia sẻ thêm về công việc hằng ngày và những thách thức trong công việc của mình. Bạn nào sẵn lòng chia sẻ vui lòng reply email này, nhóm sẽ sắp xếp một buổi phỏng vấn để tìm hiểu về công việc của bạn để viết lại và gửi đến cho cộng đồng.
📰Những bài viết hay
How LinkedIn Serves over 4.8 Million Member Profiles per Second
(by quangle80)
LinkedIn vừa giới thiệu một kỹ thuật có tên gọi Couchbase as a centralized caching tier (tận dụng Couchbase cho để làm tầng bộ nhớ cache tập trung) để giải quyết bài toán scaling cho tính năng đọc member profile, nhằm đáp ứng việc xử lý lưu lượng truy cập ngày càng tăng đến các cụm database cluster của hệ thống.
Nhiều năm qua, LinkedIn xây dựng tính năng member profiles bằng cách truy vấn trực tiếp vào Espresso document platform - một nền tảng được xây dựng trên MySQL, Apache Helix, Databus (LinkedIn’s change capture system) và sử dụng Avro để chuẩn hóa dữ liệu. Espresso routers sẽ điều phối các requests read/write đến đúng các cụm lưu trữ và sử dụng off-heap cache (OHC) cho các hot keys.
Thử thách đặt ra là vào lúc cao điểm, hệ thống phải xử lý hơn 4,8 triệu RPS, hệ thống Espresso thì đã đạt đến giới hạn về khả năng mở rộng. Thay vì làm lại các thành phần cốt lõi của nền tảng Espresso, nhóm đã chọn giải pháp xây dựng một tầng caching mới. Tầng caching mới này được tích hợp vào Espresso, kết hợp OHC hiện có và tận dụng các sức mạnh của Couchbase như khả năng chịu lỗi, cache data availability và ngăn chặn tình trạng data divergence. Lúc này, tất cả dữ liệu member profiles được lưu vào bộ nhớ cache ở mọi data-center và được cập nhật bởi các Apache Samza jobs. Các thay đổi về dữ liệu đến từ thao tác ghi được captured bởi các cụm Espresso Storage, hay các database snapshots định kỳ sẽ được Cơ chế Couchbase Compare-And-Swap (CAS) ghi nhận và đi update lại cache nếu cần.
Theo số liệu thống kê từ đội ngũ, giải pháp mới này đạt tỉ lệ thành công hơn 99%, cắt giảm hơn 60% độ trễ và tiết kiệm 10% chi phí hạ tầng hằng năm. Để tìm hiểu chi tiết hơn về bài viết cũng như lý do chọn Couchbase cho tầng Caching, mời bạn đọc tham khảo bài viết.
💭Góc bạn đọc
Thứ hai - ngày của planning
Sáng đầu tuần khởi động bằng hai cuộc họp sprint planning cho hai team đang phụ trách. Thường thứ hai sẽ được dùng cho sprint planning hoặc backlog grooming, xen kẽ mỗi tuần. Tuỳ vào lượng công việc mà có thể mình sẽ bỏ qua cuộc họp này mà chỉ theo dõi tình trạng backlog sau khi mọi người trong team đã họp xong.
Ở team mình, một sprint sẽ kéo dài hai tuần. Đầu sprint mọi người sẽ cùng duyệt qua backlog để chọn ra các task quan trọng cần phải hoàn thành trong sprint này. Sau đó mọi người cũng sẽ duyệt qua các project quan trọng và điều chỉnh lịch release dự kiến nếu cần thiết. Thông thường, backlog khá dài nên việc review các task và độ ưu tiên sẽ được làm trước trong các buổi backlog grooming của tuần trước đó để khi làm sprint planning thì sẽ không tốn thời gian review độ ưu tiên lại.
Chiều thứ hai thường sẽ có cuộc họp giao ban ngắn giữa oncall tuần trước và oncall tuần này. Ở công ty mình, tại một thời điểm sẽ luôn có người phụ trách trực cho một service hoặc một platform, người này sẽ được gọi là oncall. Tuỳ team mà ca trực oncall theo ngày hoặc theo tuần. Nhiệm vụ của oncall sẽ là theo dõi tình hình hệ thống, phản ứng khi có các cảnh báo sự cố hoặc vấn đề xảy ra, trả lời các câu hỏi của team khác liên quan đến các phần hệ thống của team mình. Trong buổi họp giao ban này, người phụ trách oncall tuần trước sẽ điểm qua những vấn đề phát sinh của tuần rồi và các điều cần lưu ý tiếp.
Trong chiều thứ hai này team mình có một buổi họp đột xuất để review một Post-mortem được viết bởi một bạn trong team, trình bày về nguyên nhân dẫn đến sự cố service bị nghẽn trong 2 ngày liên tục, mỗi lần nghẽn diễn ra trong 5 phút dẫn đến một loạt các service liên quan cũng bị nghẽn theo. Mọi người sẽ cùng review Post-mortem của bạn phụ trách và đưa ra góp ý, phản biện cũng như đặt các câu hỏi để làm rõ hơn về nguyên nhân sự cố.
Sự cố này thú vị vì sau khi điều tra cụ thể thì phát hiện ra mặt dù dấu hiệu ban đầu của việc bị nghẽn trong 2 ngày là giống nhau, nhưng nguyên nhân sâu xa gây ra lại hoàn toàn khác nhau. Ở ngày đầu tiên, service bị nghẽn vì một node trong cụm server không phục vụ được traffic cho các service khác dẫn đến mất cân bằng tải. Lý do là trong quá trình node này khởi động thì quá trình đăng ký node này vào danh mục service discovery bị lỗi, dẫn đến cụm server có 3 servers nhưng chỉ có 2 servers phục vụ được traffic thông qua service discovery. Điều này dẫn đến thuật toán auto-scaling của cụm server vốn dựa vào CPU trung bình không hoạt động đúng.
Ở ngày thứ hai, service bị nghẽn cũng vì một node trong cụm không phục vụ được traffic cho các service khác thông qua ELB nhưng do quá trình khởi tạo và đánh nhãn bị lỗi nên không bị thay thế kịp lúc. Quá trình này cũng dẫn đến mất cân bằng tải.
Thời gian còn lại của buổi chiều có thể xen kẽ các session 1:1 ngắn với các thành viên đang quản lý trực tiếp, gián tiếp hoặc đang mentor.
(còn tiếp)
👨💻Góc lập trình
(by ndaadn and phucnh)
Đề ra tuần này: Queue Reconstruction by Height
Cho một mảng people biểu diễn một hàng người, với people[i] = [h_i, k_i], trong đó người ở vị trí thứ i có chiều cao h_i, và có chính xác k_i người đứng phía trước với chiều cao bằng hoặc cao hơn h_i.
Ví dụ: people[1] = [5, 2] nghĩa là người ở vị trí số 1 có chiều cao là 5, và có 2 người đứng phía trước.
Hiện nay các phần tử trong mảng people đang bị xáo trộn, hãy sắp xếp lại mảng people sao cho giá trị của các phần tử là đúng với yêu cầu trên.
Ví dụ:
Input: people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
Output: [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
Giải thích:
Người ở vị trí số 0 có chiều cao 5, và có 0 người đứng trước có chiều cao >= 5.
Người ở vị trí số 1 có chiều cao 7, và có 0 người đứng trước có chiều cao >= 7.
Người ở vị trí số 2 có chiều cao 5, và có 2 người đứng trước có chiều cao >= 5.
Người ở vị trí số 3 có chiều cao 6, và có 1 người đứng trước có chiều cao >= 6.
Người ở vị trí số 4 có chiều cao 4, và có 4 người đứng trước có chiều cao >= 4.
Người ở vị trí số 5 có chiều cao 7, và có 1 người đứng trước có chiều cao >= 7.
Lời giải tuần trước: Find the Divisibility Array of a String
Đề bài
Do sơ suất trong quá trình biên tập, nội dung đề bài được giới thiệu ở số newsletter kỳ trước chưa chính xác, các bạn có thể tham khảo lại đề bài đã được điều chỉnh lại ở đây (thay n số tự nhiên thành n chữ số)
Cho một chuỗi word bao gồm n chữ số và một số tự nhiên m.
Trả về kết quả là mảng div với tính chất như sau:
div[i] = 1 nếu giá trị số học của chuỗi word[0…i] chia hết cho m
div[i] = 0 nếu ngược lại.
Ví dụ:
Input: word = "998244353", m = 3
Output: [1,1,0,0,0,1,1,0,0]
Giải thích: có 4 chuỗi con: "9", "99", "998244", and "9982443" thỏa mãn đề bài.
Lời giải
Đề bài yêu cầu kiểm tra tất cả các tiền tố xem có chia hết cho số m hay không, vì vậy ta cần tìm tất cả các tiền tố, tạm gọi là prefix trong chuỗi word
, và lần lượt kiểm tra xem prefix có chia hết cho số m
hay không bằng phép toán module: prefix mod m == 0.
Do độ dài của chuỗi word <= 10^5, chuỗi tiền tố sẽ vượt quá phạm vi biểu diễn của máy tính.
Mặc dù nhiều ngôn ngữ lập trình cung cấp thư viện để biểu diễn số nguyên lớn như BigInteger trong Java, việc khởi tạo và tính toán với số nguyên lớn đòi hỏi nhiều tài nguyên.
Để tối ưu bài này, ta có thể phân tích như sau:
Gọi số n
là số chia, m
là số bị chia, r
là số dư. Như vậy n = k*m + r
, với k
là kết quả của phép chia lấy phần nguyên của n
và m
.
Ta muốn thêm chữ số d
vào sau số n
, và kiểm tra xem số mới có chia hết cho m
hay không? Ta có:
Vì 10*k*m
luôn luôn chia hết cho m
, ta chỉ cần kiểm tra 10*r + d
.
Ta thực hiện giải thuật như sau:
import java.math.BigInteger;
class Solution {
public int[] divisibilityArray(String word, int m) {
int n = word.length();
int[] ans = new int[n];
long rem = 0;
for (int i = 0; i < n; i++) {
rem = (rem * 10 + word.charAt(i) - '0') % m;
if (rem == 0) {
ans[i] = 1;
}
}
return ans;
}
}
Độ phức tạp về thời gian của giải thuật là O(n) với n là độ dài của chuỗi word.
Quotes
“Wonder is the beginning of wisdom.” - Socrates