ersten Problem
Sobald Sie File.read(file)
mit einer großen Datei verwenden, wird Ihr Skript eine Menge Speicher (möglicherweise zu viel) verwenden. Sie lesen die gesamte Datei in eine riesige Zeichenfolge, obwohl CSV
Zeile für Zeile gelesen wird.
Es kann gut funktionieren, wenn Sie Dateien mit Tausenden von Zeilen verwenden. Trotzdem sollten Sie CSV.foreach verwenden.
ändern
CSV.parse(File.read(file), headers: true) do |row|
zu
CSV.foreach(file, headers: true) do |row|
In this Beispiel ging die Speicherauslastung zu 0,5 MB von 1 GB.
zweites Problem
parts_db
wird zu einem großen Array von Teilen, die bis zum Ende der CSV-Datei wachsen. Sie müssen entweder die Transaktion entfernen (der Import ist langsam, erfordert aber nicht mehr Speicher als für 1 Zeile) oder die CSV-Verarbeitung in Stapeln durchführen.
Hier ist eine Möglichkeit, es zu tun. Wir verwenden CSV.parse
wieder, aber nur mit Chargen von 2000 Zeilen:
def self.import_parts_db(filename)
time = Benchmark.measure do
File.open(filename) do |file|
headers = file.first
file.lazy.each_slice(2000) do |lines|
Part.transaction do
rows = CSV.parse(lines.join, write_headers: true, headers: headers)
parts_db = rows.map do |_row|
Part.new(
part_num: row_hash['part_num'],
description: row_hash['description'],
manufacturer: row_hash['manufacturer'],
model: row_hash['model'],
cage_code: row_hash['cage_code'],
nsn: row_hash['nsn']
)
end
Part.import parts_db
end
end
end
puts time
end
end
3. Problem?
Die vorherige Antwort sollte nicht viel Speicher verwenden, aber es könnte noch lange dauern, alles zu importieren, möglicherweise zu viel für einen Remote-Server.
Der Vorteil der Verwendung eines Enumerators besteht darin, dass es einfach ist, Stapel zu überspringen und nur die gewünschten zu erhalten.
Angenommen, Ihr Import dauert zu lange und wird aus irgendeinem Grund nach 424000 erfolgreichen Importen beendet.
Sie können ersetzen:
file.lazy.each_slice(2000) do |lines|
von
file.lazy.drop(424_000).take(300_000).each_slice(2000) do |lines|
die ersten 424.000 CSV Zeilen zu überspringen, und die nächsten 300000 diejenigen analysieren.
Für den nächsten Import verwenden:
file.lazy.drop(424_000+300_000).take(300_000).each_slice(2000) do |lines|
und dann:
file.lazy.drop(424_000+2*300_000).take(300_000).each_slice(2000) do |lines|
...
Aber oben wurde empfohlen '.foreach' .. würde es in diesem Fall nicht funktionieren? – gemart
'CSV.foreach' gibt kein Enumerable zurück, was wir für' each_slice' benötigen. Solange Sie 'File.read()' nicht verwenden, können Sie 'CSV.parse' verwenden. Im zweiten Fall ist es nur für 2000 Zeilen. –
Ich habe dein Beispiel benutzt und nach 424.000 Importen habe ich einen 'ETIMEDOUT: read ETIMEDOUT' Fehler von der heroku Konsole bekommen. Weißt du, wie ich das umgehen kann? – gemart