橋本商会 - Cosense
https://scrapbox.io/shokai
shokai / 階層整理型WiKiはスケールしない / 発表資料 / Scrapboxをグループでどう使っていくか? / ServiceWorkerとCacheによるSPAの高速化、オフラインモード / 文芸的データベース - 文書の作成とそのデータベース化を同時に行なう手法 / Pixel 4a + 物理キーボード / node-mongodb-native / 最終的に見つかればよい / 物
フィード

Pixel 4a + 物理キーボード - 橋本商会 - Cosense
橋本商会 - Cosense
Ewin bluetooth折りたたみキーボードを買った Pixel 4aに接続して使ってみているcontrolとcapslockの入れ替え https://play.google.com/store/apps/details?id=info.informationsea.apps.deletekeyasbackspace /uochan/AndroidでCtrlとCapsLock入れ換え Pixel 6aでは必要なかった。OS標準機能でcontrolとcapslock入れ替えできたshokai.icon英語・日本語の切り替え shift + space まだ慣れないshokai.icon 日本語変換の選択候補、今どれ選んでるのか見づらい 選択候補のフォントがほんの少し太くなるんだけど、ほとんど差がないので全然わからんalt + tabでアプリ切り替えできるの便利scrapbox
19時間前

node-mongodb-native - 橋本商会 - Cosense
橋本商会 - Cosense
Node.js用のMongoDB clientライブラリ https://github.com/mongodb/node-mongodb-native https://www.npmjs.com/package/mongodbnpm名はmongodbだが、node-mongodb-nativeと呼んだ方が区別しやすいのでGitリポジトリ名からそう呼んでいますshokai.icon って書いてたのに最近は自分でもmongodb npmって呼んでたmongooseも内部ではこれを使ってmongodb serverと通信している
18日前

最終的に見つかればよい - 橋本商会 - Cosense
橋本商会 - Cosense
整理整頓は必要ない。整理するのは、この2つが目的だと思うが 1. 後で取り出す速度を上げる 2. 完成させる為に、欠損を見つける 整理整頓はあくまで手段のはずなのに、目的になりがちだと思う 整理とか無駄なのでやめましょう 別の方法でも、同じ目的は十分達成できるので後で取り出す速度を上げるみんなこれに極端に最適化しすぎだと思う 1 stepか2 stepで取り出せるようにしがち 1 step 完全な定位置を決める 2 step
21日前

物を空中に固定する - 橋本商会 - Cosense
橋本商会 - Cosense
ネオジウム磁石マグネットフック 机の裏に吊るす 電源タップもフックで吊るす クランプ クランプ型の電球ソケット クランプ型の電源タップ クランプで棚にフックを増やす ホームセンターで売ってるやつ
25日前

MongoDBのcursorで出てくるdocumentは、読み出し時点で古い内容になっている可能性がある - 橋本商会 - Cosense
橋本商会 - Cosense
前提知識 mongooseのfind query cursorは色々な回し方があるけどどれ使えばいいの?_idを使ってもう一度読み出すようにした方がいい場合があるshokai.icon code:js let cursor try { cursor = model.find(condition).cursor(); for await (const { _id } of cursor) { const doc = await model.findOne({ _id }); // _idを使ってもう一度読み出す } } catch (err) { // 例外処理 } finally { cursor?.close(); }
1ヶ月前

noCursorTimeout - 橋本商会 - Cosense
橋本商会 - Cosense
MongoDB query cursorで使えるフラグ MongoDBサーバーには、アイドル状態のcursorをkillする仕組みがある 判定は、一定時間getMoreを発行しない事 cursorTimeoutMillisで設定されている これをやらなくなるオプションが、noCursorTimeout: true アプリケーション側から見ると cursorのタイムアウト(MongoServerError: Cursor id (id) not found)が ほぼ発生しなくなる 絶対に発生しないわけではない 詳しくはMongoDBのCursor not foundエラーやnoCursorTimeoutオプションとの向き合い方を読んでくれshokai.icon
1ヶ月前

MongoError: Cursor not found - 橋本商会 - Cosense
橋本商会 - Cosense
1つのcursorを長時間回していると発生するMongoError batch処理などで発生する Mongoose 8やMongoDB 8ではMongoServerError: Cursor id (id) not foundという名前のエラーになっているaddCursorFlag('noCursorTimeout', true)である程度は解決できるが https://mongoosejs.com/docs/api/querycursor.html#querycursor_QueryCursor-addCursorFlag http://mongodb.github.io/node-mongodb-native/2.2/api/Cursor.html#addCursorFlag https://github.com/nota/scrapbox/pull/4175 安易に頼らない方がいいshokai.icon 特にbatch処理では、どうしても必要なケースもあるが そもそも、どう対策してもMongoDBのfail overやclusterの構成変更時などに発生するものなので、レジュームやリトライ等の戦略から練っておく必要がある 詳しくはMongoDBのCursor not foundエラーやnoCursorTimeoutオプションとの向き合い方にまとめたshokai.icon
1ヶ月前

MongoServerError: Cursor id (id) not found - 橋本商会 - Cosense
橋本商会 - Cosense
mongooseとMongoDBサーバーそれぞれバージョン7あたりから表示されるエラー それ以前のバージョンではMongoError: Cursor not foundという表記だったbatch処理を書く上で、避けて通れないエラーである 対応方法はMongoDBのCursor not foundエラーやnoCursorTimeoutオプションとの向き合い方にまとめましたshokai.icon
1ヶ月前

mongoose 6以降ではcursor()呼び出さずにqueryを直接for await...ofで回せる - 橋本商会 - Cosense
橋本商会 - Cosense
.cursor()を使わないMongoDB query cursorの回し方がある https://mongoosejs.com/docs/api/querycursor.html#QueryCursor.prototype[Symbol.asyncIterator]() code:js const fileQuery = GCSFile.find({ categories: { $exists: false } }) for await (const file of fileQuery) { (略) }この書き方はmongoose 5の若めのバージョンでは使えなかったはずだが、mongoose 6では使えるshokai.icon cursorにオプションを渡す必要が無くて 回してる途中でreturnしたり、エラー発生しないなら、こっちの方が良いだろう使い分けはmongooseのfind query cursorは色々な回し方があるけどどれ使えばいいの?へ
1ヶ月前

mongo query cursorはfor awaitで回せる - 橋本商会 - Cosense
橋本商会 - Cosense
node 12以降なら、MongoDB query cursorはfor await...ofで回せる それまではcursor.next()がnullになるまで判定してがんばって回してたcontentType: "image/png"をcategories: ["image", "png"]の様に分割するbatchの例 code:js const cursor = GCSFile.find({ categories: { $exists: false } }) .cursor() .addCursorFlag('noCursorTimeout', true) for await (const file of cursor) { // console.log(file) const { contentType, _id } = file const categories = contentType ? contentType.split('/') : [] console.log(_id, contentType, categories) await GCSFile.update({ _id }, { $set: { categories } }) } await cursor.close()
1ヶ月前
webサーバーではnoCursorTimeoutは使うな - 橋本商会 - Cosense
橋本商会 - Cosense
batch処理ならいい。webサーバーではやめとけshokai.icon 処理に時間がかかってMongoServerError: Cursor id (id) not foundが発生する問題を解決したい そういう時にnoCursorTimeoutオプションを付けても、実は解決にならないそれどころか、アプリケーションとMongoDBサーバー両方でのリソースリークの原因になる詳しくはMongoDBのCursor not foundエラーやnoCursorTimeoutオプションとの向き合い方を見てくれ やるならbatchSize調整、単純に高速化する、レジュームやリトライ機構を必ず実装する、など
1ヶ月前

disconnectIfNotUserがnextを呼ばずに終了しているので、socket.io内のMapにTCP socketとsessionが残り続ける - 橋本商会 - Cosense
橋本商会 - Cosense
from scrapbox.ioのみ時間経過でメモリ使用量が増加する原因を、Codex CLIとともに探るdisconnectIfNotUserがnext()もnext(err)も呼ばずに終了しているので、socket.io内のMapにTCP socketとsessionが残り続けてメモリリークしているCodex CLI.iconsocket.ioの内部実装 node_modules/socket.io/dist/namespace.js:208-237 で新規ソケットを _preConnectSockets に入れたままミドルウェアのチェイン完了を待ち、完了時にのみ _doConnect() もしくはsocket._cleanup() → _preConnectSockets.delete() が呼ばれるつくりになっています。つまり next() か next(err) が必ず実行されないと、ソケットとその req/セッション一式が永遠に Map に残留します。自前ミドルウェア src/server/sockets/middlewares/index.js:18-27 の disconnectIfNotUser は socket.user が無いときに socket.disconnect(); socket.conn.close(); return; としており、next() も next(err) も呼ばずにチェインを打ち切っています。 この場合、前述の _preConnectSockets からエントリが除去されないので、認証に失敗した接続が発生するたびにソケットオブジェクト(および Mongoose セッション、HTTP リクエストなど)が GC されずに溜まり続けます。 socket.conn.close() は Engine.IO コネクションを閉じるだけで、Namespace._add() 側のコールバックには到達しないため socket._cleanup() も _preConnectSockets.delete() も実行されません(同ファイル node_modules/socket.io/dist/namespace.js:220-234 参照)。
1ヶ月前

RSS出力はクソデカXMLを作る可能性がある - 橋本商会 - Cosense
橋本商会 - Cosense
特にCosenseの場合 デカいページがたくさんあるprojectでは、デカいRSSができる RSSはメモリ上に1つの文字列として作られる node.jsにstreamでRSSを作れるライブラリが無い為 これがまずい。クソデカ文字列ができるshokai.iconRSSは、Cosenseではけっこうアクセスされている機能である (ここにnewrelicのスクリーンショット)RSSはぜんぜんオワコンではない
1ヶ月前

inspectorを使わずにNode.jsのメモリリークを見つけて修正した - 橋本商会 - Cosense
橋本商会 - Cosense
productionサーバーでしか発生しないメモリリークを、当たりをつけて(力技で)調査する方法を確立したshokai.icon事例:Cosenseのweb dynoのメモリ使用量が爆発してるけどなぜかなんともない 普通のnodeのプログラムならnode:inspectorで覗くのが良いのだが 無理だったnode:inspectorが使えない理由 productionで雑にinspectorを有効化するわけにはいかない。性能が大きく下がるから scrapbox.ioでしかメモリリークが発生していない Cosenseは他にもセミオンプレ等のproduction環境があるが、そちらでは問題発生していない productionに来ているリクエストをミラーリングしたり、ごく一部だけ調査用環境に流すような事はできない Herokuなので 一時的にinspectorを起動したり、メモリダンプを出力したりする隠しAPIを用意する? dynoを指定して外部から隠しAPIを叩くのは難しい。Herokuなので メモリ使用量を自己点検し続け、既定値を超えていたらheap snapshotを出力するという方法 これはアリかもしれない。次回に向けて検討したいshokai.icon
1ヶ月前

Node.jsはHeroku dynoに搭載されているメモリ量を誤認している - 橋本商会 - Cosense
橋本商会 - Cosense
Node.jsの中のV8の中のlibuvは、OSの物理メモリ総量をuv_get_total_memory()で取得している Node.jsのos.totalmem()は、この関数を呼び出しているos.totalmem() / 1024 / 1024 / 1024 byteを1024で3回割ってGBにしている メモリ16GBのMacbook Airで実行すると、16が返る しかしHerokuのStandard-1X dynoで実行すると 約61.79GBが返る 大きすぎる 実際には0.5GBなのに どうやらHerokuでos.totalmem()を実行すると、dynoのホストマシンの物理メモリ総量が返るようだこのせいで、Herokuで動くnodeはV8の上限である1.5GB近くまでメモリを使ってしまう。自由に使っていいメモリがあると思っているのでこれはHerokuに限った話ではない Docker等のcgroupを使った環境では、uv_get_total_memory()は正しい値を返せない
1ヶ月前

RSS - 橋本商会 - Cosense
橋本商会 - Cosense
feedの一種 RDF Site Summary、Rich Site Summary、Really Simple Syndicationなどの略Resident Set Sizeの略もRSS psコマンドやprocess.memoryUsageで見る
1ヶ月前

Cosenseのweb dynoのメモリ使用量が爆発してるけどなぜかなんともない - 橋本商会 - Cosense
橋本商会 - Cosense
totalが2GBを超えた場合、すぐにギリギリ2GB以内に戻る swapが1GBを超えた場合も、すぐにギリギリ1GB以内に戻る継続的にR14 - Memory Quota Exceededが発生している たまにR15 - Memory Quota Vastly Exceededが発生するhttps://nota.slack.com/archives/C01R3LZ6SSZ/p1761617560786359 などでも報告しているが 昔からたまにこういう事はあった nodeが自身のメモリ使用量を環境に合わせて自己判断するのは、Node.js v12から入った仕様である Node.js v12はheap out of memoryエラーがすぐ出る Scrapboxの--max-old-space-sizeを1024から指定無しに変更したい NodeとHerokuのどっちかの実装が原因で、うまく動いてない時期もあった Node 20はHerokuの共有環境のメモリ制限をうまく解釈できていないっぽいという話もある つまりheroku側の仮想環境がOSのレイヤーを通してプロセスに提供している情報が間違っている? いや、そんな事はない気がするなshokai.icon
1ヶ月前

Fir - 橋本商会 - Cosense
橋本商会 - Cosense
Herokuの次世代App環境 Cedarの次の環境 2025/4に発表された 2025/8時点で、使えるのはHeroku Private Spaceだけhttps://devcenter.heroku.com/articles/generations#firhttps://www.heroku.com/blog/heroku-fir-generally-available-new-platform-capabilities/特徴 https://devcenter.heroku.com/articles/generations#fir-new-feature-development-roadmap Kubernetesが基盤 buildpackがCloud Native Buildpacksになった 監視やログがOpenTelemetryになった private space内に500以上のdynoを起動できる private space内部のネットワークがIPv6になった最近、様々なドキュメントにFirの場合の仕様の節が追加されていっている
1ヶ月前

CosenseのMongoDB query cursor使ってる箇所を総点検 - 橋本商会 - Cosense
橋本商会 - Cosense
from scrapbox.ioのみ時間経過でメモリ使用量が増加する原因を、Codex CLIとともに探るHelpfeel専用Cosense Export APIはMongoDB query cursorを閉じ忘れていると同じような、MongoDB query cursorの閉じ忘れ箇所を洗い出す 対策としては同じ様にfinally内でcursor.close()するか mongoose 6以降ではcursor()呼び出さずにqueryを直接for await...ofで回せるのように書くか mongo query cursorはfor awaitで回せるgit show a602aefebb01e0e1367b098ccc516ab9ab6ea199で変更を把握してshokai.icon このcommitで修正しているHelpfeel専用export API以外で、同じ様な修正が必要な場所が他あれば修正したいcommitの内容を確認しました。これはMongooseのcursorを使用しているループ内で、早期return時にcursor.close()が呼ばれずにリソースリークが発生していた問題の修正ですね。Claude Code.icon try-finallyブロックでcursorのclose()を確実に呼び出すように修正されています。 同様のパターンがないか、コードベース全体を調査します。サーバーコントローラー (4個) 1. src/server/controllers/projects/audit-logs.js - business project向け監査ログ機能 done.icon business project用監査ログの出力中にクライアント側からstreamが閉じられた場合に、MongoDB query cursorがcloseできていなかった #7939 2. src/server/controllers/gcs/delete.js - GCSファイル削除ボタン
1ヶ月前

business project向け監査ログ出力はlean()をつけるべき - 橋本商会 - Cosense
橋本商会 - Cosense
from scrapbox.ioのみ時間経過でメモリ使用量が増加する原因を、Codex CLIとともに探るAudit Logs の CSV 生成では src/server/controllers/projects/audit-logs.js:131-135 で users キャッシュに User.findOne の結果(設定やタイムスタンプなどを含む Mongoose Document)を丸ごと保持しており、必要なのはメールアドレスだけなのに大量のオブジェクトがリクエスト中ずっと残る .select('email').lean() でメール文字列だけを保持するか、事前にユニークな userId をまとめて取得してキャッシュ自体をやめるべき。src/server/controllers/projects/audit-logs.js:138-175 では Event を .lean() なしでストリームし、さらに event.data を mutate しているため、各バッチが change-tracking 付きのmongoose.Document を生成し続け、行単価のメモリが跳ね上がる。 .select(...) と .lean() を使った cursor({ batchSize }) に切り替えるとドキュメントラッパーを避けられ、ヒープへの常駐量が大幅に減る。同じ箇所でレスポンスの backpressure を res.writableLength のポーリングに頼っており、クライアントが遅いとカーソルが既定バッチ(~1000件)+ユーザキャッシュを先読みしてしまう。HTTPストリームの drain イベントに合わせて cursor.pause()/resume() を呼ぶか、バッチサイズを縮めてフラッシュ速度より先にデータが溜まらないように調整する必要がある。次のステップ 1. users のキー数やカーソルのバッチ統計、ダウンロード前後のヒープスナップショットなどの計測ログを追加し、どの割り当てが支配的か確認する。 2. lean/projection とメールのみキャッシュの試作を行い、本番相当データで /api/projects/auditlogs/... を叩き、node --inspect などでヒープ/CPU を比較する。
1ヶ月前

scrapbox.ioのみ時間経過でメモリ使用量が増加する原因を、Codex CLIとともに探る - 橋本商会 - Cosense
橋本商会 - Cosense
Cosenseのweb dynoのメモリ使用量が爆発してるけどなぜかなんともないをずっと追っているshokai.icon2025/11/9(日) 17:19の、24時間のメモリ使用量 環境変数SERVER_NODE_OPTIONSに--max-old-space-size=100を設定した後の経過観察にて撮影したもの 休日の明け方という最もアクセスが少ない時間帯なのに average totalがきれいな一次関数になっている 時間経過でじわじわ伸びるタイプのメモリリークがありそうだサーバープロセスのメモリリークをチェックしてほしい。コードを読んでshokai.icon ヒント たぶんHTTPリクエストの数に応じてではなく、純粋に時間経過でメモリ使用量が増えていくようなbugがありますSlack通知機能にメモリリークは無いshokai.iconCodex CLI.iconその通知機能以外も探してshokai.iconresponseを返す前にclient側から切断された場合、graceful shutdown timer内にrequest & response objectが残り続けるCodex CLI.icon
1ヶ月前

mongooseのfind query cursorは色々な回し方があるけどどれ使えばいいの? - 橋本商会 - Cosense
橋本商会 - Cosense
mongooseのMongoDB query cursorは4つ書き方がある 1. cursorを自力で回す const cursor = model.find().cursor()して、let doc = cursor.next()がnullになるまでforかwhileで回す 2. cursorを取り出しておいてfor await...ofで回す const cursor = model.find().cursor()して、for await (const doc of cursor) { }で回す 3. for await...ofの中でcursor()を作って回す for await (const doc of model.find().cursor()) { } mongo query cursorはfor awaitで回せる 4. cursor無しのfor await...of for await (const doc of model.find()) { } mongoose 6以降ではcursor()呼び出さずにqueryを直接for await...ofで回せる結論 1は現代のnode.js環境では使う必要ないが、とりわけ問題があるわけでもない cursor回している途中で例外が起きた場合に、プロセスごと終了していいなら、4でいい だいたいのbatch処理はこれshokai.icon 絶対に2、もしくは1を使うべきシチュエーションがある
1ヶ月前

MongoDBのCursor not foundエラーやnoCursorTimeoutオプションとの向き合い方 - 橋本商会 - Cosense
橋本商会 - Cosense
前提知識 mongooseのfind query cursorは色々な回し方があるけどどれ使えばいいの?noCursorTimeout: trueオプションは、多くの場合必要ではないshokai.icon cursorを回してgetMoreする間隔が10分を超えるなら必要 そして、少なくともwebサーバーではnoCursorTimeoutは使うなgetMoreの間隔が10分以上かかるケースとは cursorで取得したdocumentの処理に時間がかかる CPU処理やI/O待ち時間が長いまずQuery.prototype.batchSizeを小さくして、1度のgetMoreで取得するdocumentの数を減らすべき mongooseの場合 10件ずつにする model.find(condition).batchSize(10) https://mongoosejs.com/docs/api/query.html#Query.prototype.batchSize() デフォルトは101件、もしくは合計16MBまで、の小さい方が選択される 1回の取得量が減ると
1ヶ月前

MongoDB - 橋本商会 - Cosense
橋本商会 - Cosense
https://docs.mongodb.com/manual/release-notes/https://www.mongodb.com/support-policy/lifecycles
1ヶ月前

ResizeObserver - 橋本商会 - Cosense
橋本商会 - Cosense
https://developer.mozilla.org/ja/docs/Web/API/ResizeObserverコツ ResizeObserverのcallback関数は、observeした直後(次のフレーム)に必ず呼ばれる 変化する前のサイズは分からないので、差分が知りたい時は、前回のwidth/heightを保持しておく必要がある
1ヶ月前

IntersectionObserver - 橋本商会 - Cosense
橋本商会 - Cosense
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_APIある要素が画面上に見えているかどうかを監視できるブラウザAPI
1ヶ月前

mongodb npm - 橋本商会 - Cosense
橋本商会 - Cosense
MongoDB公式のNode.js用のMongoDB clientライブラリ https://github.com/mongodb/node-mongodb-native https://www.npmjs.com/package/mongodb MongoDB NodeJS Driverとも呼ばれている https://www.mongodb.com/docs/drivers/node/current/mongooseも内部ではこれを使ってmongodb serverと通信しているschema定義やvalidatorが不要ならmongoose不要ではある 一般的なアプリケーションではあまり無い状況だがまた、複数のMongoDB serverに同時に接続しなければならない場合は、逆にmongooseは使えない schema定義とDB接続が密結合しており、2つの異なるDBにschemaが適用されてしまうから
1ヶ月前

async iterator - 橋本商会 - Cosense
橋本商会 - Cosense
for await...ofで回せるiterator nextで次のobjectを取得したり、yieldなどでも回せるが、for await...ofが楽で良いと思うshokai.iconhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator
1ヶ月前

for await...of - 橋本商会 - Cosense
橋本商会 - Cosense
for await...of - JavaScript | MDN code:js for await (variable of iterable) { statement }async iteratorとも呼ばれるNode.js v12から使える
1ヶ月前