2010-07-23 26 views
5

Là một phần của ứng dụng Rails của tôi, tôi đã viết một nhà nhập khẩu nhỏ hút dữ liệu từ hệ thống LDAP của chúng tôi và nhồi nhét nó vào bảng Người dùng. Thật không may, mã liên quan đến LDAP rò rỉ một lượng lớn bộ nhớ trong khi lặp lại trên 32 nghìn người dùng của chúng tôi và tôi không thể tìm ra cách khắc phục sự cố.Rò rỉ bộ nhớ trong Ruby net/ldap Module

Sự cố có vẻ liên quan đến thư viện LDAP theo một cách nào đó, như khi tôi xóa các cuộc gọi đến công cụ LDAP, mức sử dụng bộ nhớ ổn định một cách độc đáo. Hơn nữa, các đối tượng được tăng sinh là Net :: BER :: BerIdentifiedStringNet :: BER :: BerIdentifiedArray, cả hai phần của thư viện LDAP.

Khi tôi chạy quá trình nhập, mức sử dụng bộ nhớ cuối cùng đạt mức cao hơn 1 GB. Tôi cần phải tìm một số cách để sửa mã của tôi nếu vấn đề là có, hoặc để làm việc xung quanh các vấn đề bộ nhớ LDAP nếu đó là nơi mà vấn đề nằm. (Hoặc nếu có một thư viện LDAP tốt hơn cho hàng nhập khẩu lớn cho Ruby, tôi mở cửa cho đó là tốt.)

Dưới đây là các bit thích hợp của mã của tôi chúng tôi:

require 'net/ldap' 
require 'pp' 

class User < ActiveRecord::Base 
    validates_presence_of :name, :login, :email 

    # This method is resonsible for populating the User table with the 
    # login, name, and email of anybody who might be using the system. 
    def self.import_all 
    # initialization stuff. set bind_dn, bind_pass, ldap_host, base_dn and filter 

    ldap = Net::LDAP.new 
    ldap.host = ldap_host 
    ldap.auth bind_dn, bind_pass 
    ldap.bind 

    begin 
     # Build the list 
     records = records_updated = new_records = 0 
     ldap.search(:base => base_dn, :filter => filter) do |entry| 
     name = entry.givenName.to_s.strip + " " + entry.sn.to_s.strip 
     login = entry.name.to_s.strip 
     email = login + "@txstate.edu" 
     user = User.find_or_initialize_by_login :name => name, :login => login, :email => email 
     if user.name != name 
      user.name = name 
      user.save 
      logger.info("Updated: " + email) 
      records_updated = records_updated + 1 
     elsif user.new_record? 
      user.save 
      new_records = new_records + 1 
     else 
      # update timestamp so that we can delete old records later 
      user.touch 
     end 
     records = records + 1 
     end 

     # delete records that haven't been updated for 7 days 
     records_deleted = User.destroy_all(["updated_at < ?", Date.today - 7 ]).size 

     logger.info("LDAP Import Complete: " + Time.now.to_s) 
     logger.info("Total Records Processed: " + records.to_s) 
     logger.info("New Records: " + new_records.to_s) 
     logger.info("Updated Records: " + records_updated.to_s) 
     logger.info("Deleted Records: " + records_deleted.to_s) 

    end 

    end 
end 

Cảm ơn trước sự giúp đỡ nào/con trỏ!

Nhân tiện, tôi cũng đã hỏi về điều này trong diễn đàn hỗ trợ net/ldap, nhưng không nhận được bất kỳ gợi ý hữu ích nào ở đó.

+0

Bạn đang hủy liên kết chuỗi kết nối ở đâu? ldap.unbind? – Mike

+0

Xin chào Mike, Các tài liệu không bao gồm một phương pháp không liên kết, cũng như không có mã mẫu nào, vì vậy tôi đã nhận ra rằng không cần thiết. (http://net-ldap.rubyforge.org/) Bên cạnh đó, người ta sẽ không unbind cho đến khi sau khi lặp đi lặp lại thông qua các hồ sơ nào, phải không? Rò rỉ bộ nhớ đang xảy ra trong quá trình lặp lại. Tôi đánh giá cao việc động não. –

+0

Bộ dữ liệu được trả về từ tìm kiếm này bao nhiêu? Tôi giả sử dữ liệu có thể bị trùng lặp một hoặc hai lần. Phiên bản Ruby cũng có thể hữu ích. Hơn nữa, bạn có thể chia sẻ 'bộ lọc' bạn đang sử dụng không? Cuối cùng, không có khả năng xảy ra trường hợp, nhưng tôi đã thấy thư viện ldap trên các nền tảng khác thực hiện rất nhiều lần lặp trên các nhóm lồng nhau - tôi chỉ nhận ra khi nhìn vào một kết nối TCP ... – Brian

Trả lời

8

Một điều rất quan trọng cần lưu ý là bạn không bao giờ sử dụng kết quả của cuộc gọi phương thức. Điều đó có nghĩa rằng bạn phải vượt qua :return_result => false-ldap.search:

ldap.search(:base => base_dn, :filter => filter, :return_result => false) do |entry| 

Từ các tài liệu: "Thời gian: return_result => sai, #search sẽ trở lại chỉ một Boolean, để cho biết các hoạt động thành công này có thể cải thiện hiệu suất với. các tập kết quả rất lớn, vì thư viện có thể loại bỏ từng mục nhập khỏi bộ nhớ sau khi khối của bạn xử lý nó. "

Nói cách khác, nếu bạn không sử dụng cờ này, tất cả các mục sẽ được lưu trữ trong bộ nhớ, ngay cả khi bạn không cần chúng bên ngoài khối! Vì vậy, hãy sử dụng tùy chọn này.

+0

Khối trả về một tập hợp các số nguyên. Đây là một con trỏ tốt nhưng tôi nghi ngờ đó là thỏa thuận lớn được mô tả. –

+0

Tôi rephrased câu đầu tiên để "kết quả của cuộc gọi phương pháp" thay vì "kết quả của khối", vì đó là những gì là quan trọng. Nhưng tôi chân thành nghĩ rằng điều này sẽ dẫn đến một sự ứng biến tuyệt vời. –

+0

Daniel, bạn nói đúng. Tôi vừa thử nghiệm với một truy vấn trả về ~ 50000 kết quả. Với: return_result => false, máy khách vẫn ở mức khoảng 50MB RAM trong khi xử lý kết quả khi nó lên đến ~ 600MB mà không có tham số này. –