12/08/2018, 15:48

Những lỗi cơ bản thường gặp với lập trình viên Rails (Phần II)

Tiếp tục bài viết phần trước, hôm nay mình sẽ gửi đến các bạn những lỗi cơ bản thường gặp tiếp theo. Lỗi này là một hệ quả tất yếu từ sai lầm thứ 3 mà mình đã từng đề cập ở bài trước . Như đã thảo luận, Rails đặt trọng tâm vào các thành phần được đặt tên ( model, view và controller). Điều này ...

Tiếp tục bài viết phần trước, hôm nay mình sẽ gửi đến các bạn những lỗi cơ bản thường gặp tiếp theo.

Lỗi này là một hệ quả tất yếu từ sai lầm thứ 3 mà mình đã từng đề cập ở bài trước . Như đã thảo luận, Rails đặt trọng tâm vào các thành phần được đặt tên ( model, view và controller). Điều này khá tốt khi định nghĩa một thứ gì đó thuộc về các class của những thành phần này. Nhưng có đôi khi chúng ta có thể cần các hàm không thuộc về cả model, view hay controller. Hàm generators của Rails tạo nên một thư mục helper và một class helper mới với mỗi thành phần mà chúng ta tạo ra. Nó như là khuyến khích chúng ta nhồi nhét bất kỳ chức năng nào mà không phù hợp với model, controller hay view vào trong những class helper này. Rails là một trung tâm xoay quanh MVC, không có gì ngăn cản bạn tự tạo ra những class của riêng mình và thêm những chỉ dẫn thích hợp để lưu giữ code trong các class này. Khi bạn có một chức năng bỏ sung, hãy suy nghĩ về việc nhóm các hàm lại với nhau và tìm ra một cái tên thật sự rõ ràng cho class mà sẽ lư trứ những hàm này. Sử dụng một framwork toàn diện như Rails không phải là một cái cớ cho việc sử dụng những hàm, class một cách bừa bãi (mặc dù rails có thể tự hiểu :v ).

RoR được hỗ trợ bằng một thư viện gem rất phong phú và đa dạng. Điều này rất tốt cho việc xây dựng một ứng dụng phức tạp một cách nhanh chóng. Tuy nhiên bên cạnh đó cũng tồn tại rất nhiều ứng dụng cồng kềnh với hằng hà sa số gem được sử dụng trong nó trong khi các chức năng mà ứng dụng đem lại lại không hề tương xứng với số lượng gem đã được dùng. Điều này dẫn đến một vấn đề của Rails, việc sử dụng quá nhiều gem làm cho kích thước của một ứng dụng Rails lớn hơn nhiều so với thực tế cần thiết. Nó có thể làm chậm quá trình sản xuất, gây thất vọng cho người dùng và có thể khiến cho cấu hình cần thiết cho bộ nhớ máy chủ và chi phí hoạt động tăng lên. Nó cũng khiến quá trình phát triển, bảo trì và việc test tự động ( bằng rspec chảng hạn) tốn nhiều thời gian hơn cần thiết. Hãy lưu ý rằng mỗi gem mà bạn dùng trong ứng dụng có thể phụ thuộc vào gem khác và những gem này lại phụ thuộc vào những gem khác nữa. Thêm một gem vào có thể sẽ dẫn đến một hiệu ứng kết hợp các gem khác. Ví dụ như khi bạn cài gem rails_admin sẽ đồng thời cài 11 gem , tăng 10% so với việc cài đặt Rails cơ bản. Khi bài viết này được viết thì bản cài đặt Rails 4.1.0 đã bao gồm 43 gem trong trong tệp Gemfile.lock. Bao gồm cả những gem trong gemfile và những gem mà Rails tiêu chuẩn đưa vào như một sự phụ thuộc. Cân nhắc thật kỹ xem gem mà bạn muốn thêm vào có thực sự cần thiết và phù hợp với ứng dụng của mình không. Ví dụ như các lập trình viên sẽ thường thêm gem rails_admin bởi vì nó sẽ cung cấp một giao diện web tương đối ổn cho cấu trúc model tuy nhiên nó lại không phải là một công cụ duyệt cơ sở dữ liệu tốt. Nhay cả khi ứng dụng của bạn yêu cầu người dùng admin có các đặc quyền nhưng tất nhiên là bạn sẽ không bao giờ muốn cũng cấp quyền truy cập cơ sở dữ liệu thô cho họ và sẽ tốt hơn nếu bạn phát triển chức năng quản trị hơn là phụ thuộc vào gem này.

Mặc dù hầu hết cacs lập trình viên đều biết rằng log files có sẵn trong quá trình phát triển và sản xuất nhưng họ thường không quan tấm đến thông tin trong các file này. Mặc dù nhiều ứng dụng dựa vào các công cụ theo dõi như Honeybadger hay New Relic nhưng điều quan trọng là bạn phải theo dõi các log files trong quá trình phát triển và kiểm thử ứng dụng của mình. Như đã đề cập trong các phần trước, Rails framwork cung cấp cho bạn rất nhiều chức năng tiện ích, đặc biệt là trong model. Định nghĩa rõ ràng các mối liên kết trong model của bạn sẽ khiến cho việc sử dụng những mối quan hệ dữ liệu trở nên dễ dàng hơn kể cả khi dùng trong views. Tất cả những phần SQL cơ bản cần thiết cho model của bạn đều được Rails tự động. Điều đó thật tuyệt nhưng làm sao bạn có thể chắc chắn rằng những phần SQL này thực sự hiệu quả? Một lỗi SQL mà bạn thường xuyên mắc phải đó là lỗi N+1 query (nghe rất quen phải không). Để tìm hiể rõ về lỗi này, cách duy nhất là các bạn phải xem xét các truy vấn SQL trong log files của mình. Ví dụ, bạn có 1 đoạn querry sau dùng trong 1 ứng dụng blog đơn giản để hiển thị các comment của 1 số bài posts:

def comments_for_top_three_posts
  posts = Post.limit(3)
  posts.flat_map do |post|
    post.comments.to_a
  end
end

Nhìn vào log files sau khi thực hiện hàm trên, chúng ta sẽ thấy một câu querry để lấy ra 3 bài posts sau đó tiếp tục là 3 câu querry để lấy ra các comment của mỗi bài post:

Started GET "/posts/some_comments" for 127.0.0.1 at 2014-05-20 20:05:13 -0700
Processing by PostsController#some_comments as HTML
  Post Load (0.4ms)  SELECT "posts".* FROM "posts" LIMIT 3
  Comment Load (5.6ms)  ELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 1]]
  Comment Load (0.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 2]]
  Comment Load (1.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 3]]
  Rendered posts/some_comments.html.erb within layouts/application (12.5ms)
Completed 200 OK in 581ms (Views: 225.8ms | ActiveRecord: 10.0ms)

Active Record trong Rails có thể làm giảm đáng kể số lượng truy vấn bằng cách cho phép bạn chỉ định trước tất cả các phần cần thiết sẽ được gọi. Điều này được thực hiện bằng cách gọi phương thức include (hoặc preload) trên ActiveRecord::Relation. Vowis include, AvtiveRecord sẽ đảm bảo tất cả các phần cần thiết sẽ được tải với số lượng truy vấn tối thiểu có thể. Ví dụ:

def comments_for_top_three_posts
  posts = Post.includes(:comments).limit(3)
  posts.flat_map do |post|
    post.comments.to_a
  end
end

Khi code được sửa lại như trên, log files đã cho ta thấy rằng tấy cả các câu comment đã được lấy ra chỉ với một câu truy vấn thay vì 3 câu như trước:

Started GET "/posts/some_comments" for 127.0.0.1 at 2014-05-20 20:05:18 -0700
Processing by PostsController#some_comments as HTML
  Post Load (0.5ms)  SELECT "posts".* FROM "posts" LIMIT 3
  Comment Load (4.4ms)  SELECT "comments".* FROM "comments" WHERE"comments "."post_id" IN (1, 2, 3)
  Rendered posts/some_comments.html.erb within layouts/application (12.2ms)
Completed 200 OK in 560ms (Views: 219.3ms | ActiveRecord: 5.0ms)

Lỗi N + 1 querry trên đây chỉ là một ví dụ về những vấn đề mà bạn có thể gặp phải nến không chú ý đúng mức trong quá trình phát triển sản phẩm. Điều mình muốn nới đến ở đây là các bạn nên kiểm tra kỹ càng quá trình phát triển sản phẩm cũng như log files để sớm phát hiện ra vấn đề nếu có và tối ưu hóa code của mình. Xem lại các log files là một cách tuyệt vời để có thể kiểm tra được những phần không hiệu quả của code từ đó chỉnh sửa cho hợp lý trước khi bàn giao cho khách hàng hay đưa vào production. Nếu không, bạn có thẻ sẽ không nhận ra được vấn đề về hiệu suất của Rails cho đến khi sản phẩm của bạn thực sự được public, do số lượng dữ liệu khi phát triển và kiểm thử là nhơ hơn nhiều so với khi thực sự được sử dụng. Nếu bạn làm việc với một ứng dụng hoàn toàn mới, có thể ban đầu khối lượng dữ liệu là nhỏ và nó khiến cho ứng dụng của bạn chạy khá là trơn tru. Nhưng sau 1 thời gian sử dụng, cùng với việc khối lượng dữ liệu ngày càng mở rộng thì tốc độ ứng dụng của bạn cũng sẽ càng ngày càng chậm hơn. Nếu bạn cảm thấy rằng các log files của bạn đang có rất nhiều các thông tin mà bạn không cần sử dụng, bạn có thể làm sạch log files tại đây (những kỹ thuật này cũng hoạt động trong development tương tự như trong production logs).

0