Quantcast
Viewing all articles
Browse latest Browse all 8691

node.jsで、gcsにあるサイズの大きいjsonlファイルを、mongodbに登録する(メモリふっとばさずに)

node.jsでgcsからファイルを読み込む

gcsからファイルを読み込む方法を探すと、よくdownload()を使用する例が紹介されています。

conststorage=newStorage();constbucket=storage.bucket('test-20180903');constfile=bucket.file('sample');file.download().then(function(data){res.status(200).json({'result':data.toString('utf-8')});});

download()だと、サイズの大きいファイルを読み込むとメモリ不足
cloud functionsだと2Gまでしかメモリ拡張できないので、gcs側にファイル配置する際に、ファイルサイズを小さく分割しながら.·゜゜·(/。\)·゜゜·.

@google-cloud/storageのソースを見ている

download()以外に、createReadStream()なるものが!

file.ts
conststorage=newStorage();constbucket=storage.bucket('my-bucket');constfs=require('fs');constremoteFile=bucket.file('image.png');constlocalFilename='/Users/stephen/Photos/image.png';remoteFile.createReadStream().on('error',function(err){}).on('response',function(response){// Server connected and responded with the specified status and headers.}).on('end',function(){// The file is fully downloaded.}).pipe(fs.createWriteStream(localFilename));

createReadStream()でらファイルを読み込む

gcsからファイルを読み込む方法を探すと、よくdownload()を使用する例が紹介されています。

conststorage=newStorage();constbucket=storage.bucket('test-20180903');constfile=bucket.file('sample');file.download().then(function(data){res.status(200).json({'result':data.toString('utf-8')});});

download()だと、サイズの大きいファイルを読み込むとメモリ不足
cloud functionsだと2Gまでしかメモリ拡張できないので、gcs側にファイル配置する際に、ファイルサイズを小さく分割しながら.·゜゜·(/。\)·゜゜·.

@google-cloud/storageのソースを見ている

download()以外に、createReadStream()なるものが!

file.ts
conststorage=newStorage();constbucket=storage.bucket('my-bucket');constfs=require('fs');constremoteFile=bucket.file('image.png');constlocalFilename='/Users/stephen/Photos/image.png';remoteFile.createReadStream().on('error',function(err){}).on('response',function(response){// Server connected and responded with the specified status and headers.}).on('end',function(){// The file is fully downloaded.}).pipe(fs.createWriteStream(localFilename));

createReadStream()で、ストリーム処理に

↑サンプルをもとに、mongodbへの登録処理を実装してみると、なぞのエラーが・・・
responseイベントは、ノンブロッキング(非同期)処理されるので、mongodbへのアクセスが多すぎたみたい

gcsからサイズの大きいjsonlファイルをmongodbに登録する

createReadStream()で作成したストリームを、ブロッキング(同期)処理したら大丈夫でした。

exports.execute=async(event,context)=>{constclient=awaitmongo.connect(process.env.MONGODB_URL,{useNewUrlParser:true,useUnifiedTopology:true})letrs=nulltry{constdb=client.db(process.env.MONGODB_DATABASE)rs=awaitstorage.bucket(bucket).file(pubsubMessage.name).createReadStream();forawait(constlineofreadLines(rs)){constjson=JSON.parse(line)json.lastModified=newDate()// 更新日時があたらしかった場合更新する constresult=awaitdb.collection(collection).replaceOne({_id:json._id,updateDttm:{$lte:json.updateDttm}},json,{upsert:false})if(result.matchedCount==0){// 未登録の場合があるので登録してみる。 try{awaitdb.collection(collection).insertOne(json)}catch(err){if(err.message.indexOf("E11000")<0){throwerr}}}}}catch(err){throwerr}finally{if(client){client.close()}if(rs){rs.destroy()}}functionreadLines(rs){constoutput=newstream.PassThrough({objectMode:true});constrl=readline.createInterface(rs,{});rl.on("line",line=>{output.write(line);});rl.on("close",()=>{output.push(null);});returnoutput;}}

for await...of 文を使うことで、ブロッキング(同期処理)にできました!
node v12からは、標準のreadline.createInterface()が、async iterable を返すようになるようなので、自前のreadLines()もいらなくなるようです。スッキリ書けますね。


Viewing all articles
Browse latest Browse all 8691

Trending Articles