3 điểm bởi GN⁺ 2023-12-08 | 1 bình luận | Chia sẻ qua WhatsApp
  • FLAME hướng tới việc mở rộng đàn hồi tinh vi bằng cách bọc một phần mã ứng dụng hiện có thành hàm và cho chạy trong một bản sao ứng dụng tạm thời, thay vì chuyển mã sang một runtime riêng
  • Cách tiếp cận FaaS hiện tại có thể tạo ra cấu trúc làm tăng thêm HTTP, S3, SQS, API Gateway, encoder/decoder và orchestration workflow ngay cả với các tác vụ như tạo thumbnail video
  • Thư viện flame của Elixir ủy quyền việc thực thi hàm cho runner từ xa bằng FLAME.call, còn FLAME.FlyBackend của Fly.io khởi động một Fly Machine mới bằng cùng Docker image và kết nối với node cha trong khoảng 3 giây
  • Khi phát triển và kiểm thử, chạy trong cùng runtime bằng LocalBackend; ở production, điều chỉnh scale-to-zero và thời gian giữ trạng thái hot ngắn bằng min, max, max_concurrency, idle_shutdown_after
  • FLAME không hẳn loại bỏ hàng đợi công việc, mà tách đảm bảo độ bền khỏi thực thi đàn hồi: hàng đợi phụ trách dispatch, commit, retry, còn tác vụ tốn CPU được xử lý bên trong lời gọi FLAME

Vấn đề mà pattern FLAME nhắm tới

  • Tự động mở rộng đàn hồi giúp giảm gánh nặng quản trị máy chủ và hứa hẹn chi phí dựa trên mức sử dụng, nhưng khi dùng FaaS, bạn có thể đồng thời phải tăng thêm hàng đợi, lưu trữ, glue code và độ phức tạp trong phát triển, kiểm thử, CI
  • FLAME là viết tắt của Fleeting Lambda Application for Modular Execution, một cách xem toàn bộ ứng dụng như lambda và chỉ chạy một số tác vụ mô-đun trên hạ tầng được bật lên trong thời gian ngắn
  • Mục tiêu có thể tóm gọn thành ba điểm
    • Giảm quản trị máy chủ bằng các luồng triển khai hiện có như fly deploy, git push heroku, kubectl
    • Chỉ mở rộng tinh vi theo nhu cầu cho các phần cụ thể của mã ứng dụng
    • Không viết lại ứng dụng hoặc chuyển một phần mã sang runtime độc quyền

Chuyển hàm hiện có bằng FLAME.call

  • Ví dụ là hàm generate_thumbnails trong ứng dụng Elixir, dùng ffmpeg để chuyển video đã upload thành thumbnail
  • Hàm hiện có tạo thư mục tạm, chạy ffmpeg, sau đó lưu các thumbnail được tạo vào kho lưu trữ bền vững và ghi URL vào DB bằng Repo.insert_all
  • Transcoding video tốn CPU có thể làm dừng toàn bộ dịch vụ trong production, vì vậy thay vì chuyển toàn bộ mã sang FaaS hoặc microservice, chỉ bọc thân hàm bằng FLAME.call(MyApp.FFMpegRunner, fn -> ... end)
  • FLAME.call nhận tên pool runner và một hàm, sau đó tìm hoặc khởi động một bản sao mới của toàn bộ ứng dụng và chỉ chạy hàm đó
    • Các biến mà hàm đóng bắt giữ, như struct %Video{}interval, được truyền tự động
    • Runner FLAME sau khi khởi động sẽ kết nối với node cha, nhận hàm cần chạy rồi trả kết quả về cho bên gọi
    • Tùy cấu hình, runner sẽ chờ thêm tác vụ rồi tắt khi idle, hoặc kết thúc ngay lập tức
  • Vì toàn bộ ứng dụng, bao gồm kết nối DB, được chạy nên có thể dùng nguyên Repo.insert_all như trong mã gốc

Độ phức tạp của FaaS và điểm khác biệt của FLAME

  • FaaS cung cấp các thành phần để giải quyết vấn đề, nhưng FLAME giống một cách tiếp cận nhằm giảm chính lớp giao tiếp riêng biệt hơn
  • Trong ví dụ thumbnail video, dù bắt đầu bằng AWS Lambda Function URL đơn giản, độ phức tạp sẽ sớm xuất hiện
    • Nếu muốn stream thumbnail trở lại ứng dụng qua HTTP, phải viết encoder và decoder tùy chỉnh ở cả hai phía
    • Nếu transcoding hoặc upload video vượt quá 15 phút, do hard timeout của Lambda, phải chia video thành các chunk và dùng thêm nhiều Lambda hơn
    • Cần thêm các dịch vụ như orchestration workflow, SQS, S3
  • Triển khai dựa trên FaaS thường tạo ra các điểm chi phí sau
    • Kích hoạt Lambda bằng HTTP endpoint, S3, API Gateway
    • Viết Lambda tùy chỉnh cho transcoding video
    • Lưu kết quả thumbnail vào SQS
    • Viết SQS consumer ở phía ứng dụng
    • Lưu vào DB và cấu hình cách gửi lại sự kiện cho các subscriber đang hoạt động được kết nối với instance khác
  • FLAME cho phép dùng nguyên mã nội bộ của ứng dụng cùng DB, PubSub và tính năng nền tảng hiện có, nhờ đó có thể giảm các dịch vụ riêng và kho lưu trữ dùng để thu hồi kết quả

Backend Fly.io và chạy cục bộ

  • Thư viện flame của Elixir là một triển khai của pattern FLAME, mặc định cung cấp LocalBackendFlyBackend
  • FLAME.FlyBackend khởi động một bản sao ứng dụng trên Machine mới trong hạ tầng Fly.io và có thể kết nối với node cha trong khoảng 3 giây để nhận tác vụ
  • Vì Fly.io chạy ứng dụng dưới dạng Docker image đã đóng gói, backend yêu cầu Fly API khởi động một Machine mới có cùng image với ứng dụng hiện tại
  • Trong hạ tầng Fly.io, runner FLAME có thể được khởi chạy ở cùng region với node cha, giúp giảm độ trễ giữa node cha và runner
  • FLAME.FlyBackend dưới 200 LOC kể cả tài liệu, và phụ thuộc thư viện duy nhất là HTTP client req
  • Khi phát triển và kiểm thử, LocalBackend chạy phần mã đó trong runtime hiện có trên laptop hoặc máy chủ CI

Truyền tệp và đơn giản hóa phát triển, kiểm thử

  • FLAME cho phép tái sử dụng business logic, cấu hình DB, PubSub và tính năng nền tảng mà không cần viết mã bên ngoài ứng dụng
  • Trong Elixir, nhờ tính năng phân tán của Erlang VM, có thể gửi file stream từ node cha tới ứng dụng FLAME từ xa
    • Mở file stream của đường dẫn video trên node cha
    • Trong FLAME con, mở stream file tạm và sao chép stream từ node cha
    • Sau đó ffmpeg dùng file tạm trên runner từ xa làm input để tạo thumbnail
  • Với cách này, có thể truyền tệp tới máy chủ FLAME mà không cần cấu hình riêng S3 hay giao diện HTTP
  • Giảm việc triển khai dịch vụ riêng, quản lý endpoint, thu hồi kết quả qua S3/SQS và cấu hình phụ thuộc cho phát triển, kiểm thử, CI

FLAME bên ngoài Elixir

  • Elixir cung cấp giám sát tiến trình và messaging phân tán nên rất phù hợp với mô hình FLAME, nhưng các ngôn ngữ có primitive đồng thời hợp lý cũng có thể dùng pattern này
  • Ví dụ proof of concept bằng JavaScript là dạng chạy hàm ứng dụng trên một machine khác từ Fly Machine: fly-run-this-function-on-another-machine
  • Luồng chung của lời gọi FLAME dựa trên JavaScript là chuyển phần thực thi mô-đun sang tệp mới và chạy trong pool runner
  • Nếu đối số có thể serialize JSON, toàn bộ luồng sẽ tương tự ví dụ Elixir: mã ứng dụng được chạy trên một instance bật lên trong thời gian ngắn
  • Một thư viện FLAME hoàn chỉnh cần xử lý
    • Logic scale-up và scale-down cho pool đàn hồi
    • Quản lý pool có xét đến hot startup và cold startup
    • Giám sát runner từ xa để tránh orphaned resource
    • Cách giữ deployment luôn ở trạng thái mới nhất

Quan hệ với hàng đợi tác vụ nền

  • FLAME cũng hoạt động bên trong trình xử lý tác vụ nền, nhưng có phần chồng lấn vai trò với hàng đợi công việc
  • Hàng đợi công việc thường được dùng khi cần đảm bảo độ bền, và hàng đợi có thể được điều chỉnh để xử lý nhiều tác vụ hơn theo biến động tải
  • Tác vụ bền vững và thực thi đàn hồi là hai mối quan tâm riêng biệt
    • Nếu dùng hàng đợi chỉ cho mục đích offload thực thi đơn giản, sẽ cần glue code để đưa dữ liệu vào tác vụ và trả kết quả về cho bên gọi hoặc thiết bị người dùng
    • Nếu cần bảo đảm tạo thumbnail thành công sau khi upload video, hàng đợi có thể phụ trách cơ chế dispatch, commit, retry
    • Phần transcoding thực tế có thể chạy bằng lời gọi FLAME bên trong tác vụ, tách độ bền khỏi thực thi có khả năng mở rộng
  • Những tác vụ không cần độ bền, như xem trước video trước khi lưu hoặc chạy mô hình ML khi người dùng đã rời ứng dụng, có thể không phù hợp với cấu trúc ghi tác vụ vào kho lưu trữ bền vững

Pool runner cho mở rộng đàn hồi

  • Triển khai FLAME của Elixir định nghĩa pool đàn hồi của runner, hỗ trợ đồng thời scale-to-zero và giới hạn đồng thời
  • Cấu hình ví dụ thêm FLAME.Pool vào start/2 của ứng dụng
    • Cho phép scale-to-zero với min: 0
    • Khởi động tối đa 10 runner với max: 10
    • Hỗ trợ 5 tác vụ ffmpeg trên mỗi runner với max_concurrency: 5
    • Idle down nếu không có lời gọi tác vụ trong 30 giây với idle_shutdown_after: 30_000
  • Máy chủ web Phoenix được khởi động có điều kiện tùy theo sự hiện diện của FLAME parent
    • Không cần khởi động webserver trên runner FLAME không xử lý web traffic
    • Các thành phần như MyApp.Repo vẫn được giữ nguyên vì cần dùng bên trong runner FLAME
  • Nếu đặt min: 1, có thể duy trì ít nhất một runner ffmpeg ở trạng thái hot ngay từ lúc ứng dụng khởi động

Bố trí tiến trình có trạng thái

  • Phần có trạng thái của ứng dụng Elixir được xây dựng quanh primitive tiến trình nhẹ có message mailbox
  • FLAME.callFLAME.cast phù hợp với mã tương đối stateless, còn FLAME.place_child khởi động một process specification hiện có trên runner FLAME thay vì cục bộ
  • FLAME.place_child có thể được dùng ở những chỗ sử dụng các interface như Task.Supervisor.start_child, DynamicSupervisor.start_child
  • Trong ví dụ tạo thumbnail khi upload bằng LiveView, các chunk upload được truyền tới tiến trình ThumbnailGenerator, và tiến trình này giao tiếp với ffmpeg
    • Khi tìm thấy PNG delimiter trong stdout của ffmpeg, nó gửi thông điệp hình ảnh tới tiến trình LiveView
    • LiveView nhận thông điệp trong handle_info và thêm hình ảnh mới vào UI
  • Khi thay lời gọi DynamicSupervisor.start_child(@sup, spec) hiện có bằng FLAME.place_child(Thumbs.FFMpegRunner, spec), tiến trình ThumbnailGenerator sẽ chạy trên runner FLAME
  • Vì các tiến trình có thể gửi và nhận thông điệp bất kể vị trí, khi tiến trình kết thúc do upload chấm dứt hoặc tab trình duyệt bị đóng, máy chủ FLAME phát hiện việc kết thúc và idle down khi không còn tác vụ khác

Giám sát từ xa và xử lý lỗi

  • Hạ tầng bật lên trong thời gian ngắn cần failsafe để tránh orphaned resource
  • Khi parent khởi chạy runner, runner phải tự idle down khi không có tác vụ, và nếu không còn liên lạc được với node cha thì phải xử lý failsafe shutdown
  • Khi parent bị thay thế bởi deployment mới, runner cũng phải kết thúc để bảo đảm cụm đang chạy cùng một phiên bản mã
  • Các caller đang chờ kết quả tác vụ runner phải tính đến tình huống runner bị tắt vì bất kỳ lý do nào
  • Các primitive do Erlang VM cung cấp giúp triển khai việc này đơn giản hơn
    • Giám sát process cục bộ/từ xa và supervision
    • Node monitoring để phát hiện node được bật lên và tắt xuống
    • Luồng shutdown kiểm soát thứ tự khởi động/kết thúc ứng dụng, cho các runner đang hoạt động thời gian hoàn thành tác vụ khi có deployment mới
  • Chi tiết triển khai nội bộ sẽ được tiếp tục trong bài viết riêng; hiện có thể xem source của flame

Trạng thái hiện tại và bước tiếp theo

  • Thư viện FLAME cho Elixir vẫn ở giai đoạn đầu nhưng có thể thử ngay bây giờ
  • Trong tương lai dự kiến sẽ có phân tích sâu hơn về các kỹ thuật tăng trưởng pool nâng cao và cách triển khai bằng Elixir
  • Cũng có thể thảo luận về việc triển khai pattern FLAME trong các ngôn ngữ khác

1 bình luận

 
GN⁺ 2023-12-08
Ý kiến trên Hacker News
  • Sau khi trải qua nỗi đau và độ phức tạp của một ứng dụng gồm hơn 100 hàm Lambda trong 4 năm qua, tôi thấy bài viết này chỉ đúng vào nhược điểm của kiến trúc serverless FaaS
    Khi mới bắt đầu, những nhược điểm này không dễ thấy. Ngược lại, nếu mức sử dụng thấp thì gần như miễn phí và gần như không cần bảo trì, đó là lợi thế rất rõ
    Chỉ về sau, khi workflow Lambda trở nên chằng chịt và ngày càng cứng nhắc vì các phụ thuộc lẫn nhau, bạn mới hối tiếc rằng lẽ ra nên đi theo monolith và tự quản lý với thêm vài trăm đô la. Ngày nay dùng những nơi như fly.io có khi còn tốn ít hơn
    Tôi tự hỏi nếu không dùng Elixir thì sẽ thế nào

    • Tôi liên tục thấy một mẫu hình, và giờ nó gần như giống một đúc kết hay quy luật: các lập trình viên có nhiệt huyết có thể khiến gần như bất kỳ quy trình hay kiến trúc nào vận hành được trong khoảng 18 tháng
      Đến lúc tình hình xấu đi thì cũng thường là lúc nên tìm việc mới. Điều này càng đúng nếu khoảng 1 năm sau khi vào làm, bạn bắt đầu hối tiếc về quy trình do chính mình đưa vào. Tôi đã thấy điều đó nhiều lần với các sếp tệ, các “unit test” lộn xộn, scrum, v.v.
      Tuy vậy, tôi không biết mọi người có nhận ra rõ nguyên nhân gây khó chịu trong công việc hay chỉ cảm nhận mơ hồ là “đến lúc rời đi rồi”. Khi cố gắng gọi tên điều gây khó chịu, tôi gặp rất nhiều phản ứng ngược, và sau khi đọc Good to Great thì tôi bớt hao tổn cảm xúc vì chuyện đó hơn nhiều. Không ai muốn nói “à, đây là hệ quả từ hành vi của mình”
      Những người thực sự tạo ra hệ thống kiểu Rube Goldberg mà tôi đang bảo trì là những người rời đi đầu tiên. Người thuyền trưởng của con tàu đó hỏi đồng nghiệp rằng liệu có nên open-source engine của chúng tôi không, và đồng nghiệp trả lời rằng không ai muốn dùng một hệ thống đã phát minh lại cái bánh xe vốn đã tồn tại và còn tốt hơn. Người đó tự nguyện rời đi trong vòng 3–4 tháng
    • Chúng tôi đang xây dựng thứ có thể giải quyết vấn đề này trong bất kỳ ngôn ngữ nào. Hiện đã có TypeScript SDK và Java cũng đang được phát triển
      Nếu có thể viết mã hướng dịch vụ thông thường, nơi các Lambda có thể gọi lẫn nhau, thì không cần phải chia ứng dụng thành hàng trăm mảnh chạy ngắn. Nhưng nếu phải trả tiền cho cả thời gian chờ thì điều đó là bất khả thi. Nếu Lambda có thể tạm dừng thực thi trong lúc chờ I/O thì vấn đề được giải quyết. Vì vậy tôi nghĩ durable execution có thể là câu trả lời
      Trong vài tuần qua tôi đã viết một bài để minh họa điều này: https://restate.dev/blog/suspendable-functions-make-lambda-t...
    • Một phần trong bài cũng nói về FLAME ngoài Elixir. Tóm lại, đây là một pattern thường có thể áp dụng cho các ngôn ngữ có mô hình đồng thời hợp lý
      Có thể khó có được toàn bộ độ tiện dụng mà Elixir cho miễn phí, như các hàm có thể serialize biến đã capture, nhưng với các ngôn ngữ như JavaScript, tôi nghĩ có thể đạt khoảng 90% bằng cách chuyển phần thực thi module sang một file mới thay vì bọc bằng closure
      Người triển khai thư viện FLAME cũng phải viết phần pooling, monitoring và giao tiếp từ xa. Elixir cho sẵn rất nhiều thứ ở mảng distributed messaging và monitoring. Các tính năng liên quan đến bố trí process thực ra gần như chỉ áp dụng cho Elixir
    • Chỉ 12 Lambda thôi cũng đã khó chịu nổi rồi. Ứng dụng ban đầu được tạo bởi người không mấy để ý đến bảo trì hay triển khai, và mã bị copy-paste khắp nơi
      Cuối cùng chúng tôi chuyển sang một monolith Lambda béo, trong đó một Lambda duy nhất xử lý nhiều endpoint
    • Nếu không quá bận tâm đến cold start hoặc có thể quản lý nó, bạn có thể tạo monolith cả trên Lambda
      Nói cách khác, bạn vẫn có thể đi theo monolith đồng thời tối thiểu hóa chi phí AWS. Đây không phải là bài toán phải chọn một trong hai
      Dạo này tôi dùng asp.net, và ngay cả một ứng dụng khá lớn được triển khai ready-to-run, bao gồm cả EF model đã tối ưu, cũng khởi động tương đối nhanh
  • Tôi là tác giả bài viết. Rất vui vì cuối cùng đã công bố được, và nếu có câu hỏi tôi sẽ trả lời. Hy vọng một số người sẽ được truyền đủ cảm hứng để triển khai FLAME pattern bằng JavaScript, Go và các ngôn ngữ khác

    • Trông có vẻ hay. Mong Microsoft chú ý. Azure Functions quá phức tạp về thiết lập bảo mật và triển khai, lại đặt ra những giả định kỳ lạ về việc bạn muốn chạy loại mã nào
    • Cách triển khai tự nhiên nhất có lẽ sẽ giống vert.x trên JVM
      Nó đã có cơ chế xử lý reactive scaling và thực thi bất đồng bộ qua Future, coroutine trên một event bus, đồng thời cũng hỗ trợ serialize dữ liệu và phân tán trên toàn cluster. Ngoài ra còn có event bus client cho nhiều ngôn ngữ phổ biến, nên có thể xây dựng ứng dụng bằng cách pha trộn nhiều ngôn ngữ
    • Có lẽ nên giảm bớt sự cường điệu
      Nếu trình bày vấn đề như “…một số phận còn tệ hơn cái chết”, rồi mô tả giải pháp quá dễ và không đau đớn đến mức ai không bỏ các cách tiếp cận khác thì trông như kẻ ngốc, các bài như vậy rất dễ bị xem là bài bán hàng thuần túy. Đó là kiểu của người bán thuốc chữa bách bệnh
      Phần đầu bài đã chỉ ra vấn đề khá tốt, nên nếu có một so sánh khách quan ít cảm tính hơn nhiều, có lẽ những người tò mò về một cách tiếp cận tốt hơn sẽ ít bị phản cảm hơn
      Nội dung thực chất của bài thì thú vị, nhưng cách diễn đạt khiến tôi chùn lại. Hy vọng bạn xem đây là góp ý mang tính xây dựng
    • Bài viết và video đều hay, khái niệm cũng rất thú vị. Tôi mong chờ bản triển khai JavaScript, nhưng có vẻ không dễ
      Và tôi thấy hơi áy náy một chút vì đã giữ trước ffmpeg.fly.dev
    • Tôi tò mò không biết về bản chất, điều này khác gì so với cách Sidekiq khởi tạo codebase Rails để chạy background job
  • Đoạn “hãy tưởng tượng chỉ cần bọc bất kỳ phần nào của mã ứng dụng hiện có trong một hàm là nó sẽ tự động scale, và block mã đó được chạy trong một bản sao tạm thời của ứng dụng” rất thú vị
    Nghe giống như tạo ra phiên bản fork cho serverless. Công trình tuyệt vời

  • Vài năm trước tôi từng dùng một dịch vụ về cơ bản làm đúng việc này. PiCloud đáng tiếc đã bị Dropbox mua lại, nhưng trước đó nó có đúng mô hình kiểu này: phân tán tác vụ một cách trong suốt sang các worker. Cách làm là đóng gói mã rồi chạy trên worker
    Ví dụ ở đây. Có thể thấy đó là đúng cùng một mô hình: https://github.com/picloud/basic-examples/blob/master/exampl...
    Tôi chưa từng dùng Elixir, nhưng đã dùng Erlang từ mấy chục năm trước, và BEAM có vẻ về cơ bản không thay đổi nhiều. Vì đây là một phần cốt lõi của thiết kế nên có lẽ nó phù hợp hơn nhiều cho loại công việc này. Dù vậy, cũng không phải là bữa trưa hoàn toàn miễn phí; tôi nghĩ vẫn có khả năng tiến trình chính bị chết trong lúc chờ

  • Đồng ý với toàn bộ lập luận. Ở https://www.windmill.dev chúng tôi chọn một cách tiếp cận khác: xem đơn vị trừu tượng hóa ở cấp mã nguồn, chứ không phải cấp container
    Chúng tôi phân tích hàm main và các import để trích xuất đối số và phụ thuộc, rồi chạy nguyên mã trong runtime mong muốn (TypeScript, Python, Go, Bash). Bí quyết cốt lõi là quản lý cache hiệu quả để worker luôn ở trạng thái nóng bất kể import là gì
    Cách này không tích hợp vào codebase sâu như FLAME, nhưng nhóm người dùng mục tiêu khác nhau. Người dùng của chúng tôi tạo từ đầu các workflow phức tạp, cron job, hoặc script dùng một lần có UI tự động sinh kèm theo
    Trong FLAME, có vẻ toàn bộ context được snapshot rồi khôi phục lại trên VM đích. Một cách tiếp cận khác là đưa vào cú pháp để chỉ định context nào cần và context nào không, nhằm chỉ tải mức tối thiểu. Chúng tôi hiện đang khám phá hướng này để tích hợp Windmill tốt hơn với các codebase hiện có và không phải phụ thuộc vào HTTP call

    • Điều thực sự xảy ra trong FLAME không hẳn là như vậy. FLAME chỉ gọi hàm trên node từ xa bằng tính năng clustering tích hợp của BEAM
      Trong quá trình đó, việc chỉ truyền context cần thiết được xử lý ngầm. Bài viết cũng giải thích như sau: “FLAME.call nhận tên pool runner và một hàm. Sau đó nó tìm hoặc khởi động một bản sao mới của toàn bộ ứng dụng, rồi chạy hàm ở đó. Các biến mà hàm capture trong closure, chẳng hạn như cấu trúc %Video{} và interval, sẽ tự động được truyền kèm”
    • Bạn gõ thừa một chữ w. URL cho những ai đang tìm là đây: https://www.windmill.dev/
      Tôi thích mục tiêu của dự án. Thật sự mong Windmill trở thành một phương án mã nguồn mở thay thế Retool/Airtable tốt hơn
  • Tôi thích điểm “trong FLAME, runner cho phát triển và kiểm thử chỉ chạy trên backend cục bộ”. Serverless mà có trải nghiệm phát triển cục bộ ổn thì hay đấy

    • Đúng vậy. Một trong những lý do tôi không thích serverless là vì trải nghiệm phát triển cục bộ tệ hơn nhiều so với chạy một monolith
  • Nếu “sau đó nó tìm hoặc khởi động một bản sao mới của toàn bộ ứng dụng và hàm đó được chạy ở đó”, thì mỗi lần gọi Flame.call là khởi động mới toàn bộ tiến trình ứng dụng rồi sao chép execution context vào đó à?
    Về mặt mở rộng thì đây là một giải pháp rất đơn giản, nhưng có vẻ cũng sẽ có nhược điểm
    Nếu thời gian khởi động ứng dụng tăng thêm 10ms, thì coi như mọi điểm Flame.call trong ứng dụng đều bị cộng thêm 10ms, và bộ nhớ chắc cũng tương tự
    Có lẽ khi dùng hệ thống này cần cân nhắc những lo ngại đó

    • FLAME.Pool ở phần sau bài viết xử lý điểm này. Các runner được pooling, được giữ ở trạng thái nóng trong khoảng thời gian có thể cấu hình theo ý muốn, rồi hạ xuống khi nhàn rỗi
      Khi có tải, pool đã được làm nóng sẵn nên gần như không phải trả chi phí cold start. Tiếp theo, chúng tôi cũng đang thêm các kỹ thuật tăng trưởng pool tinh vi hơn vào thư viện Elixir, để tránh cả tình huống gặp runner đã đầy rồi phải cold start runner mới
      Với runner nóng, phần overhead chỉ là độ trễ giữa cha và con. Vì chúng phải ở cùng data center, nên sẽ khoảng 1ms hoặc thấp hơn
  • Tuyệt vời. Nó có cảm giác như một phiên bản rất nhẹ chỉ dành cho Elixir của thứ chúng tôi đã xây dựng ở https://www.inngest.com/
    Cả hai đều giống nhau ở chỗ bọc mã hiện có bằng một thứ gì đó để có thể dùng trong serverless function, và về bản chất làm cho nó có thể được gọi như RPC từ xa
    Loại mã này thường chạy như một chuỗi các bước mệnh lệnh. Mỗi bước có thể được chạy tuần tự hoặc song song như các Lambda bổ sung. Nhưng giữa các bước có trạng thái ngầm được capture trong biến. Vì vậy hàm trở thành một workflow. Trong mô hình Inngest, trạng thái này được capture rồi tiêm lại vào hàm để có tính bền vững
    Về mặt durability, các tiến trình như vậy nên dựa trên queue. Điểm hay của mô hình này là queue rẻ. Nếu biến queue thành thứ rẻ như một dòng mã, mọi thứ sẽ dễ dàng hơn. Bất kỳ developer nào cũng có thể viết mã đáng tin cậy mà không phải lo về hạ tầng
    Monitoring và observability cũng quan trọng. Dead letter queue thật sự rất khủng khiếp, và cần có khả năng quản lý cũng như chạy lại các hàm hoặc bước bị lỗi
    FLAME và Inngest cũng có khác biệt. Inngest dựa trên queue, hướng sự kiện, và có thể được cung cấp qua HTTP từ bất kỳ ngôn ngữ nào. Vì Inngest lưu trạng thái bên ngoài, bạn có thể viết workflow bằng Elixir rồi viết lại bằng TypeScript và redeploy, còn hàm đang chạy vẫn có thể được di chuyển trực tiếp qua lại giữa các ngôn ngữ backend theo kiểu tương tự CRIU
    Nếu theo hướng event-driven thì cũng có thể kiểm soát luồng. Debounce, batch, throttling, đến fan-out đều có thể xử lý trong bất kỳ runtime hay ngôn ngữ nào. Ví dụ, một ứng dụng Elixir trên Fly có thể gửi event để chạy một hàm trên TypeScript + Lambda
    Rất mong xem FLAME sẽ đi đến đâu. Tôi thấy có những phần mục tiêu khá giống nhau

    • Inngest trông như một dịch vụ rất hay. Bài viết cũng đã nói đến trình xử lý tác vụ, durability và retry
      Khi cần durability, retry và workflow trong Elixir, thường người ta dùng Oban, và ở đây cũng sẽ tiếp tục làm như vậy. Job Oban sẽ gọi FLAME để xử lý phần thực thi đàn hồi
  • Đây là một trong những lý do tôi thật sự ghét kiểu viết hoa tiêu đề theo phong cách Mỹ mà HN áp đặt. Serverless trông như thể đang nói về việc suy nghĩ lại công ty serverless.com với chữ S viết hoa, chứ không giống đang nói về việc suy nghĩ lại nguyên tắc serverless với chữ s viết thường
    Nói thêm thì, tôi cũng mong có ai đó suy nghĩ lại về Serverless

    • Tôi nghĩ kiểu viết hoa không phải do HN áp đặt, mà do người đăng bài quyết định chứ nhỉ
      Không biết bạn đã xem https://sst.dev/ chưa, nhưng có vẻ họ đã làm đúng việc đó. Ví dụ, họ có Live Lambda Development, giúp phát triển cục bộ thật sự dễ dàng bằng cách rút ngắn đáng kể vòng lặp phản hồi, không cần phải đưa mã lên cloud rồi chờ triển khai
  • Ý tưởng khá hay và API cũng tuyệt vời
    Về đoạn “các tác vụ bị giới hạn bởi CPU như transcoding video có thể nhanh chóng làm dừng toàn bộ dịch vụ trong production”, chẳng phải chỉ cần tự động mở rộng theo CPU là được sao?

    • Ý này tôi đã định đề cập ở phần đầu bài. Vấn đề của cách tiếp cận đó là nó mở rộng ở sai cấp độ vận hành
      Để xử lý một tác vụ nóng cụ thể, bạn sẽ phải mở rộng toàn bộ ứng dụng, bao gồm cả web server. Điều chúng ta muốn, và cũng là lý do tìm đến FaaS, là mở rộng đàn hồi ở mức chi tiết. Ý tưởng ở đây là cho phép kiểu mở rộng chi tiết đó trên mã ứng dụng hiện có, thay vì cứ bấm liên tục nút mở rộng web server hay worker rồi hy vọng mọi thứ ổn
    • Vừa đúng vừa không. Phần workload còn lại có thể không cần nhiều CPU. Có thể chỉ một hoặc hai workload mới cần mức hiệu năng này, và bạn muốn các tác vụ đó không bị những việc khác lấn át
      Hoặc cũng có thể cần GPU
      Dịch vụ lõi có thể chỉ cần 1–2 máy chủ là đủ, nhưng với một tác vụ chỉ phát sinh khoảng một lần mỗi ngày, bạn có thể cần mở rộng lên hàng chục, hàng trăm, thậm chí hàng nghìn máy khi cần