Como parte de la aplicación Rails, escribí un pequeño importador que toma datos de nuestro sistema LDAP y los almacena en una tabla de usuarios. Desafortunadamente, el código relacionado con LDAP filtra grandes cantidades de memoria mientras itera sobre nuestros usuarios de 32K, y no he podido encontrar la manera de solucionar el problema.Pérdida de memoria en el módulo de Ruby net/ldap
El problema parece estar relacionado con la biblioteca LDAP de alguna manera, ya que cuando elimino las llamadas a LDAP, el uso de memoria se estabiliza bien. Además, los objetos que están proliferando son Net :: BER :: BerIdentifiedString y Net :: BER :: BerIdentifiedArray, ambos parte de la biblioteca LDAP.
Cuando ejecuto la importación, el uso de la memoria finalmente alcanza un máximo de más de 1GB. Necesito encontrar alguna manera de corregir mi código si el problema está allí, o solucionar los problemas de memoria LDAP si es allí donde reside el problema. (O si hay una mejor biblioteca de LDAP para grandes importaciones de Rubí, estoy abierto a eso también.)
Aquí está la parte pertinente de nuestra mi código:
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
Gracias de antemano por cualquier ayuda/punteros!
Por cierto, pregunté sobre esto también en el foro de soporte de net/ldap, pero no obtuve ningún puntero útil allí.
¿Dónde está desvinculando la cadena de conexión? ldap.unbind? – Mike
Hola Mike, Los documentos no incluyen un método de desvinculación, ni ninguno de los códigos de muestra, por lo que pensé que no era necesario. (http://net-ldap.rubyforge.org/) Además, uno no se desvincularía hasta después de iterar a través de los registros de todos modos, ¿verdad? La pérdida de memoria se produce durante la iteración. Aprecio la lluvia de ideas. –
¿Qué tan grande es el conjunto de datos devuelto por esta búsqueda? Supongo que los datos pueden duplicarse una o dos veces. La versión de Ruby puede ser útil también. Además, ¿puedes compartir el 'filtro' que estás usando? Finalmente, no es el caso, pero he visto que las bibliotecas de ldap en otras plataformas hacen mucha iteración en grupos anidados. Solo me di cuenta al mirar un volcado de TCP de la comunicación ... – Brian