2012-02-08 8 views
5

1.6gbのxmlファイルがあります.Sax Machineで解析すると、ストリーミングやチャンクでファイルを食べていないようです私のルビープロセスが2.5GB以上のRAMを登っているので、ファイル全体をメモリにロードすることができます(または、おそらくどこかでメモリリークがありますか?)。私は記憶がなくなったので成長が止まるところは分かりません。大きなファイルをSaxMachineで解析するとファイル全体がメモリにロードされているようです

小さいファイル(50MB)ではファイル全体が読み込まれているようです。私のタスクはxmlファイルのレコードを繰り返し処理し、各レコードをデータベースに保存します。 「アイドリング」が約30秒かかると、突然データベースクエリが実行を開始します。

私は、SAXがこのような大きなファイルをメモリにロードせずに作業できると思っていました。

私は見落としていることはありますか?

感謝

アップデートあなたが見ることができるように

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 

のサンプルコードを追加するためには、私は、飼料中の<listings>要素を気にしないでください。私はそれぞれの属性が欲しいだけです。<listing>要素。

出力は次のようになります。

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

単純な答えあなたの質問に:コードの怠惰な、繊維ベースのバージョンを使用するには、私はあなたが、特にこのようなあなたのgemfileでgregweb'sバージョンを参照する必要があると思うはい、あなたが一望できているものがあります。残念ながら、あなたはそれが何であるか教えてくれませんでした。誰も目に見えないコードでメモリリークを見つけることはできません。 –

+0

@MichaelKayサンプルを追加しました。ありがとう – jakeonrails

答えて

2

、それは一定のメモリを使用するように、私はサックスマシンをフォーク:https://github.com/gregwebs/sax-machine

グッドニュース:私の変更をマージすることを計画している新しいメンテナがあります。 自分自身と新しいメンテナーが今年1年間問題なく私のフォークを使用しています。

+0

このブランチは標準リポジトリと同期していないようで、2年後には触れられていません。また、ルートファイバから収穫することについてのエラーを投げていた... –

+0

私も "(FiberError)ルートファイバから収穫することはできません"エラー、この枝が放棄されたように見えます。 – doomspork

0

あなたは正しいです、SAXMachineは熱心に文書全体を読み込みます。 https://github.com/pauldix/sax-machine/blob/master/lib/sax-machine/sax_handler.rb

あなたの問題を解決するために、http://nokogiri.rubyforge.org/nokogiri/Nokogiri/XML/SAX/Parser.htmlを直接使用し、ハンドラを自分で実装します。

+0

私の疑惑を確認していただきありがとうございます。その恥ずかしそうなサックスマシンは、怠惰な評価や真のコールバックメカニズムを提供しません。それは素晴らしいことです。 – jakeonrails

3

ここでリストの要素は入れ子にすることができ、あなたが解析したい場合は、メモリ

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 

に文書全体をロードせずに、各リストを処理することができるように、ブロックに各リストのXMLを生成しますリーダーです

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 

あなたはそれが猿、パッチを適用したら、それは、それぞれが個別にリストに対処するのは簡単です:

単一のドキュメントとして最も外側のリストは、あなたがこれを行うことができます
+0

素晴らしいです、それはうまく働きます。私のローカルマシン上のdbがインポートのボトルネックになるので、かなり速いようです。ありがとう、ジョン! – jakeonrails

+0

私はこのアプローチを使って大規模なXML文書を標準的なサックス・マシンの宝石と一緒に解析することができました。ありがとう! –

3

sax-machineの残念なことに、threedifferentreposがあります。そして、悪いことに、gemspecのバージョンは打たれませんでした。

Greg Weber's blogにコメントがありましたが、私はこのコードがpauldixまたはezklのフォークに統合されているとは思わないのです。

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

あなたが正しいかのように見えます。 Githubネットワークグラフ(https://github.com/gregwebs/sax-machine/network)は、Gregの変更が正式なSAXMachineリポジトリに統合されていないことを示しています(pauldixによって維持管理されています) – Ivar

関連する問題