2012-02-08 32 views
5

Tengo un archivo xml de 1.6 gb, y cuando lo analizo con Sax Machine no parece estar transmitiendo o comiendo el archivo en fragmentos, sino que aparece para cargar todo el archivo en la memoria (¿o quizás hay una pérdida de memoria en alguna parte?) porque mi proceso de rubí sube más de 2,5 gb de ram. No sé dónde deja de crecer porque me quedé sin memoria.Analizando un archivo grande con SaxMachine parece que está cargando todo el archivo en la memoria

En un archivo más pequeño (50mb) también parece estar cargando todo el archivo. Mi tarea itera sobre los registros en el archivo xml y guarda cada registro en una base de datos. Tarda unos 30 segundos en "inactividad" y, de repente, las consultas de la base de datos comienzan a ejecutarse.

Pensé que SAX se suponía que le permitiera trabajar con archivos de gran tamaño como este sin cargar todo en la memoria.

¿Hay algo que estoy pasando por alto?

Muchas gracias

actualización para añadir código de ejemplo

class FeedImporter 

    class FeedListing 
    include ::SAXMachine 

    element :id 
    element :title 
    element :description 
    element :url 

    def to_hash 
     {}.tap do |hash| 
     self.class.column_names.each do |key| 
      hash[key] = send(key) 
     end 
     end 
    end 
    end 

    class Feed 
    include ::SAXMachine 
    elements :listing, :as => :listings, :class => FeedListing 
    end 

    def perform 
    open('~/feeds/large_feed.xml') do |file| 

     # I think that SAXMachine is trying to load All of the listing elements into this one ruby object. 
     puts 'Parsing' 
     feed = Feed.parse(file) 

     # We are now iterating over each of the listing elements, but they have been "parsed" from the feed already. 
     puts 'Importing' 
     feed.listings.each do |listing| 
     Listing.import(listing.to_hash) 
     end 

    end 
    end 

end 

Como se puede ver, no me importa el elemento <listings> en la alimentación. Solo quiero los atributos de cada elemento <listing>.

La salida tiene el siguiente aspecto:

Parsing 
... wait forever 
Importing (actually, I don't ever see this on the big file (1.6gb) because too much memory is used :(
+0

Simple respuesta a tu pregunta: sí, hay algo que están pasando por alto. Desafortunadamente no nos has dicho de qué se trata. Nadie puede encontrar pérdidas de memoria en el código que no puede ver. –

+0

@MichaelKay He agregado una muestra. Gracias – jakeonrails

Respuesta

2

I bifurcado Sax-máquina para que utilice la memoria constante: https://github.com/gregwebs/sax-machine

Buenas noticias: hay un nuevo responsable que está planeando en la fusión de mis cambios. Yo y el nuevo mantenedor hemos estado usando mi tenedor sin problemas desde hace un año.

+0

Esta rama parece estar fuera de sincronización con el repositorio canónico y no se ha tocado en dos años. También arrojaba errores sobre el rendimiento de una fibra raíz ... –

+0

Yo también obtengo el error "(FiberError) no puede ceder de la fibra raíz", parece que esta rama ha sido abandonada. – doomspork

0

Tiene usted razón, SAXMachine lee todo el documento con impaciencia. Eche un vistazo a sus fuentes de controladores: https://github.com/pauldix/sax-machine/blob/master/lib/sax-machine/sax_handler.rb

Para resolver su problema, usaría http://nokogiri.rubyforge.org/nokogiri/Nokogiri/XML/SAX/Parser.html directamente e implementaría el manejador usted mismo.

+0

gracias por confirmar mi sospecha. Es una máquina de saxofón vergonzosa que no hace una evaluación perezosa o proporciona un verdadero mecanismo de devolución de llamada, eso sería espléndido. – jakeonrails

3

Aquí es un lector que rendirá XML de cada anuncio a un bloque, por lo que puede procesar cada inmueble sin cargar todo el documento en la memoria

reader = Nokogiri::XML::Reader(file) 
while reader.read 
    if reader.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT and reader.name == 'listing' 
    listing = FeedListing.parse(reader.outer_xml) 
    Listing.import(listing.to_hash) 
    end 
end 

Si listado de elementos podría estar anidado, y que querían analizar el listados más exteriores como documentos individuales, se puede hacer esto:

require 'rubygems' 
require 'nokogiri' 


# Monkey-patch Nokogiri to make this easier 
class Nokogiri::XML::Reader 
    def element? 
    node_type == TYPE_ELEMENT 
    end 

    def end_element? 
    node_type == TYPE_END_ELEMENT 
    end 

    def opens?(name) 
    element? && self.name == name 
    end 

    def closes?(name) 
    (end_element? && self.name == name) || 
     (self_closing? && opens?(name)) 
    end 

    def skip_until_close 
    raise "node must be TYPE_ELEMENT" unless element? 
    name_to_close = self.name 

    if self_closing? 
     # DONE! 
    else 
     level = 1 
     while read 
     level += 1 if opens?(name_to_close) 
     level -= 1 if closes?(name_to_close) 

     return if level == 0 
     end 
    end 
    end 

    def each_outer_xml(name, &block) 
    while read 
     if opens?(name) 
     yield(outer_xml) 
     skip_until_close 
     end 
    end 
    end 

end 

vez que lo tienes mono-parcheado, es fácil de tratar con cada listado individual:

open('~/feeds/large_feed.xml') do |file| 
    reader = Nokogiri::XML::Reader(file) 
    reader.each_outer_xml('listing') do |outer_xml| 

    listing = FeedListing.parse(outer_xml) 
    Listing.import(listing.to_hash) 

    end 
end 
+0

Impresionante, eso funciona muy bien. Parece bastante rápido, también, ya que mi DB en mi máquina local se convierte en el cuello de botella para la importación. Gracias, John! – jakeonrails

+0

Pude analizar mi documento xml grande usando este enfoque junto con la gema de saxofón canónica. ¡Gracias! –

3

Lamentablemente ahora hay threedifferentrepos para sax-machine. Y lo que es peor, la versión de gemspec no se superó.

A pesar del comentario en Greg Weber's blog, no creo que este código esté integrado en las horquillas de pauldix o ezkl.Para utilizar la versión perezoso, a base de fibra del código, creo que es necesario hacer referencia específica a la versión gregweb's en sus Gemfile así:

gem 'sax-machine', :git => 'https://github.com/gregwebs/sax-machine' 
+0

Parece que estás en lo correcto. El gráfico de red de Github (https://github.com/gregwebs/sax-machine/network) muestra que los cambios de Greg no se han fusionado en el repositorio canónico de SAXMachine (mantenido por pauldix) – Ivar

Cuestiones relacionadas