2012-02-08 20 views
5

Mam plik xml 1,6 gb, a kiedy parsuję go w Sax Machine, nie wydaje się, żeby był streaming lub jedzenie pliku w kawałkach - raczej pojawia się do załadowania całego pliku do pamięci (a może gdzieś tam jest wyciek pamięci?), ponieważ mój proces ruby ​​wspina się w górę o 2,5 gb pamięci RAM. Nie wiem, gdzie przestaje rosnąć, bo zabrakło mi pamięci.Parsowanie dużego pliku przy użyciu SaxMachine'a wydaje się być wczytywaniem całego pliku do pamięci

Na mniejszym pliku (50mb) wygląda również na ładowanie całego pliku. Moje zadanie iteruje po rekordach w pliku xml i zapisuje każdy rekord w bazie danych. Trwa to około 30 sekund "bezczynności", a następnie nagle rozpoczyna się wykonywanie zapytań do bazy danych.

Pomyślałem, że SAX miał pozwolić ci pracować z dużymi plikami, takimi jak ta, bez ładowania wszystkiego w pamięci.

Czy jest coś, co przeoczyłem?

Dziękujemy

Aktualizacja dodać przykładowy kod

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 

Jak widać, nie dbam o elemencie w paszy <listings>. Chcę tylko atrybuty każdego elementu <listing>.

Wyjście wygląda następująco:

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

Prosta odpowiedź na pytanie: tak, coś jest nie jesteś z widokiem. Niestety nie powiedziałeś nam, co to jest. Nikt nie może znaleźć wycieków pamięci w kodzie, którego nie widzą. –

+0

@MichaelKay Dodałem próbkę. Dzięki – jakeonrails

Odpowiedz

2

I rozwidlone sax maszynę tak, że używa stałej pamięci: https://github.com/gregwebs/sax-machine

Dobra wiadomość: jest nowy opiekun, który planuje na scalanie moje zmiany. Ja i nowy opiekun używaliśmy mojego widelca bez problemu przez rok.

+0

Ta gałąź wydaje się być niezsynchronizowana z repozytorium kanonicznym i nie została dotknięta w ciągu dwóch lat. Występowały również błędy dotyczące plonowania z błonnika głównego ... –

+0

Również otrzymałem komunikat "(FiberError) nie może się wydać z włókna root", wygląda na to, że ta gałąź została porzucona. – doomspork

0

Masz rację, SAXMachine czyta cały dokument z niecierpliwością. Spójrz na jego źródła obsługi: https://github.com/pauldix/sax-machine/blob/master/lib/sax-machine/sax_handler.rb

Aby rozwiązać Twój problem, użyłbym bezpośrednio numeru http://nokogiri.rubyforge.org/nokogiri/Nokogiri/XML/SAX/Parser.html i sam wykonam procedurę obsługi.

+0

dziękuję za potwierdzenie mojego podejrzenia. To wstydliwa maszyna saksofonowa nie robi leniwego oszacowania ani nie zapewnia prawdziwego mechanizmu wywołania zwrotnego - to byłoby wspaniałe. – jakeonrails

3

Oto Reader że przyniesie XML każdego wpisu do bloku, więc można przetwarzać każdą nieruchomość bez ładowania cały dokument do pamięci

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 

Jeśli wymieniając elementy mogą być zagnieżdżone, i chciał do analizowania ofert najbardziej oddalonych pojedynczych dokumentów, można to zrobić:

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 

skoro masz to małpa-poprawione, to jest łatwe do czynienia z których każdy jest indywidualnie:

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

Niesamowite, działa super dobrze. Wydaje się też dość szybki, ponieważ moja db na mojej lokalnej maszynie staje się wąskim gardłem do importowania. Dzięki, John! – jakeonrails

+0

Udało mi się sparsować mój duży dokument xml przy użyciu tego podejścia wraz z kanonicznym klejnotem maszyny sax-machine. Dzięki! –

3

Niestety istnieje teraz threedifferentrepos dla maszyny sax. Co gorsza, wersja Gemspec nie została naruszona.

Pomimo komentarza na Greg Weber's blog, nie sądzę, że ten kod został zintegrowany z widelcami pauldix lub ezkl.Aby użyć leniwy, wersja włókien opartych kodu, myślę, że trzeba specjalnie odwoływać gregweb's wersję w swoim gemfile tak:

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

Wygląda na to, że masz rację. Wykres sieci Github (https://github.com/gregwebs/sax-machine/network) pokazuje, że zmiany Grega nie zostały połączone z kanonicznym repozytorium SAXMachine (utrzymywane przez pauldix) – Ivar