0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Xを1.4億倍非効率なファイルシステムとして使う方法

0
Last updated at Posted at 2026-05-29

はじめに 〜こんなことを真顔で考える金曜日〜

突然ですが、みなさんは GmailFS を覚えていますか?

2004年、Googleが「無料で1GBのメールボックス」を提供開始した時、世界中のギークたちは度肝を抜かれました。当時の無料Webメールといえば数MBが当たり前の時代に、1GBですよ、1GB。

そして必ず一定数いるんですよね、こういうことを言い出す人が。

「1GBのストレージがタダで使えるなら、これってもうファイルシステムじゃね?」

その狂気の発想を実装してしまったのが、Richard Jones氏作の GmailFS でした。LinuxのFUSEを使って、ファイルをチャンク分割してGmailにメールとして送信、IMAPで読み書きする。件名にメタデータを埋め込み、ラベルでディレクトリ構造を表現する。「全てのサービスはファイルシステムになりたがる」 というUNIX哲学の体現でした。

…で、ここで本題です。

「2026年の今、同じノリで作るとしたら、何をストレージにすればいい?」

私の答えは、決まりました。

X(旧Twitter)です。

凍結のリスク、APIの月額課金、利用規約。すべての困難を乗り越えた先に、ロマンがあります。本記事では、設計から実装、そして「なぜこれが1.4億倍(実は300億倍)非効率なのか」までを真面目に解説していきます。

ちなみに、最後まで読むと 実際に動くソースコード が手に入ります。WSLでもLinuxでもOKです。

この記事で呼んでいるX-FSは真面目な方のXFSとは別物です。
紛らわしくてゴメンナサイ。


第1章: そもそもファイルシステムとは何か

ファイルシステムの構成要素をざっくり整理してみましょう。

構成要素 役割
スーパーブロック FSのメタ情報(ルート位置など)
inode ファイル/ディレクトリの実体(メタデータ)
ディレクトリエントリ 名前 → inode のマッピング
データブロック 実データを保存する単位
ジャーナル 整合性のためのログ

これを、Xの世界の概念にマッピングしてみます。

FSの概念 X-FSでの対応
スーパーブロック ピン留めポスト 📌
inode ポストID(18桁)
ディレクトリエントリ リプライ関係
データブロック ポスト本文(Base64)
ハードリンク リツイート
シンボリックリンク 引用ポスト
inode検索 ハッシュタグ #inode_42
mtime ポスト作成日時
ファイル削除 ポスト削除

…ちょっと待ってください。これ、意外と全部マッピングできるんですよ。

私もここまで整理してみて、「あれ、もしかして真面目に動くのでは?」と気づいてしまいました。気づいてしまったら、もう作るしかない。


第2章: X-FSの全体設計

ストレージ階層

┌──────────────────────────────────────────────────┐
│  📌 [ピン留め] ROOT / SUPERBLOCK #x_fs_root      │  ← スーパーブロック
└──────────────────────────────────────────────────┘
            │
            ├── 💬 [リプライ] 📁 mkdir docs #x_fs_dir       ← ディレクトリ
            │       │
            │       ├── 💬 [リプライ] 📄 touch hello.txt   ← ファイル(inode)
            │       │       │
            │       │       ├── 💬 [X-FS_CHUNK:0] 44GT44KT...   ← データブロック
            │       │       ├── 💬 [X-FS_CHUNK:1] 44Gr44Gh...
            │       │       └── 💬 [X-FS_CHUNK:2] 44Gv44CB...
            │       │
            │       └── 💬 [リプライ] 📄 touch neko.jpg
            │               └── ... (5,000ポスト続く)
            │
            └── 💬 [リプライ] 📁 mkdir photos #x_fs_dir

つまり、全てがリプライツリーで表現されるわけです。あなたのファイルシステムが、誰かのスマホで通知される世界。控えめに言って最悪です。最高ですね。

チャンクサイズの決定

Xのポストは280文字制限があります。バイナリデータはBase64エンコードで詰め込みますが、

  • Base64は 3バイト → 4文字 に膨張する
  • プレフィックス [X-FS_CHUNK:9999] で約20文字消費

なので、安全圏は次の通り:

$$
\text{最大バイナリサイズ} = \frac{(280 - 20) \times 3}{4} = 195 \text{ バイト}
$$

実装では安全マージンを取って 1ポスト = 180バイト としました。


第3章: 「1.4億倍非効率」の根拠(実は控えめでした)

このセクションを書くにあたって、実際に計算してみたところ、控えめに見積もっても300億倍ぐらい非効率 であることが判明しました。タイトル詐欺すみません。

速度の試算

1GBのファイルを保存することを考えます。

必要なポスト数 = 1,073,741,824 バイト ÷ 180 バイト/post
              ≈ 5,965,232 ポスト

1GB = 約600万ポスト です。これ書き込み終わるまでに何年かかるかわかります?

X API 無料枠 (月500ポスト) で1GB保存する場合

5,965,232 ÷ 500 = 11,930ヶ月
                = 994年

994年です。 平安時代から書き込み始めて、今ようやく1GBが終わる頃合いです。源頼朝が生まれる前から書き始めて、ちょうど令和の今、書き込み完了という壮大なプロジェクト。

SSDとの比較

通常のSSDで1GB書き込みに約1秒として、

X-FS(無料枠): 994年 × 365日 × 24時間 × 3600秒 = 約3.14×10¹⁰ 秒
通常SSD:      1 秒
比率:         約 314億倍 遅い

「1.4億倍」と書きましたが、実際は約314億倍 でした。タイトルが控えめすぎて反省しています。

金額の試算

X API Basic プラン(月$200で月10,000ポスト投稿可)で同じことをやると、

5,965,232 ポスト ÷ 50 ポスト/$ = $119,305

1GBで約1,200万円 です。Google Drive 100GBが年間2,500円、AWS S3 が1GBあたり月数円であることを考えると、なかなかの強気価格設定ですね。

ストレージ効率の試算

180バイトのデータを保存するために、X側のデータベースには:

  • ポスト本文: 約260バイト
  • メタデータ(post_id, user_id, created_at, in_reply_to, lang, source, etc.): 約500バイト〜
  • インデックス、レプリケーション含めて推定: 約2KB
ストレージ膨張率: 2,000 ÷ 180 ≈ 11.1倍

実データ180バイトを保存するのに、Xのサーバには2KB書かれます。Elonさんごめんなさい。


第4章: 実装

ここからが本題です。実際にFUSEを使って実装していきます。WSL上のPythonで動かします。

⚠️ 重大な免責事項
本記事の実装は、本物のX APIには接続しません。ローカルJSONでXを完全シミュレートするモックバックエンド を使います。
理由は明白で、本物に繋いだら以下が確定するからです:

  • 連投スパム判定で即凍結
  • 月$200〜のAPI課金で破産
  • 利用規約違反による永久BAN

「自分のアカウントで挑戦してみたい!」という方は、MockXBackend クラスを差し替えるだけで動く設計にしてあります。自己責任 でどうぞ。一応墓碑銘くらいは用意しておきます。

モックバックエンド

まずはXをシミュレートするバックエンドです。json ファイルに「ポスト一覧」を貯めていきます。

class MockXBackend:
    """X APIのモック。すべてローカルJSONに保存。"""

    def __init__(self, db_path):
        self.db_path = Path(db_path)
        if self.db_path.exists():
            self.db = json.loads(self.db_path.read_text(encoding="utf-8"))
        else:
            self.db = {
                "next_id": 1000000000000000000,  # X風の18桁ID
                "posts": {},      # post_id -> {text, in_reply_to, created_at, meta}
                "pinned": None,   # ルートのポストID
            }
            self._save()

    def post(self, text, in_reply_to=None, meta=None):
        if len(text) > 280:
            raise ValueError("280文字を超えています")
        pid = str(self.db["next_id"])
        self.db["next_id"] += 1
        self.db["posts"][pid] = {
            "text": text,
            "in_reply_to": in_reply_to,
            "created_at": time.time(),
            "meta": meta or {},
        }
        self._save()
        return pid

ポイントは、280文字制限を真面目に再現している ことです。本物のXに対する敬意です。

FUSEオペレーションの実装

FUSEは「ファイルシステム操作をユーザー空間のコードで処理する」仕組みです。fusepy を使うと、Pythonで getattr, readdir, read, write といったメソッドを実装するだけでファイルシステムが作れます。

データの読み出しは、対象ファイルポストへのリプライを全部集めて、Base64デコードして連結 するだけです。

def _read_file_data(self, file_pid):
    """ファイルポストの子リプライからデータを連結して復元"""
    chunks = self.backend.get_replies(file_pid)
    data = b""
    for cid, c in chunks:
        if c["meta"].get("type") != "chunk":
            continue
        # ポスト本文の "[X-FS_CHUNK:N] " プレフィックスを剥がす
        payload = c["text"].split("] ", 1)[1]
        data += base64.b64decode(payload)
    return data

書き込みは逆向きです。既存チャンク(リプライ群)を削除して、新しいデータを180バイトずつBase64エンコードして連投します。

def _write_file_data(self, file_pid, data):
    # 既存チャンクを全削除
    for cid, c in self.backend.get_replies(file_pid):
        if c["meta"].get("type") == "chunk":
            self.backend.delete(cid)
    # 新しいチャンクを連投
    for idx, off in enumerate(range(0, len(data), CHUNK_SIZE)):
        chunk = data[off:off + CHUNK_SIZE]
        encoded = base64.b64encode(chunk).decode("ascii")
        text = f"[X-FS_CHUNK:{idx}] {encoded}"
        self.backend.post(text, in_reply_to=file_pid,
                         meta={"type": "chunk", "idx": idx})

このコードを本物のX APIに繋ぐと、ファイル1個保存するたびに数千件の連投が走り、即凍結します。素晴らしいですね。

ディレクトリ作成 = ポスト投稿

mkdir も、結局のところ「親ポストへのリプライを1件作る」だけです。

def mkdir(self, path, mode):
    parent_pid, name = self._resolve_parent(path)
    self.backend.post(
        f"[X-FS] 📁 mkdir {name} #x_fs_dir",
        in_reply_to=parent_pid,
        meta={"type": "dir", "name": name, "mode": mode}
    )
    return 0

mkdir hoge を実行すると、Xに [X-FS] 📁 mkdir hoge #x_fs_dir というポストが投稿されます。フォロワーから見たら本物のホラー ですね。


第5章: 動作確認

セットアップ

# WSL/Linux 環境で
bash setup.sh
# → fuse + fusepy を入れてくれる

起動

ターミナル1:

python3 x_fs.py /tmp/xmount

ターミナル2:

$ echo "hello X-FS" > /tmp/xmount/test.txt
$ cat /tmp/xmount/test.txt
hello X-FS
$ mkdir /tmp/xmount/photos
$ ls -la /tmp/xmount
total 0
drwxr-xr-x 1 ebe ebe 4096 May 22 12:00 photos
-rw-r--r-- 1 ebe ebe   11 May 22 12:00 test.txt

ちゃんとファイルシステムです。 Linuxから見れば、これがXに保存されているとは思いもしません。

裏側を覗く

x_fs_timeline.json を覗くと、あなたのファイル操作が「ポスト」として並んでいます。

{
  "posts": {
    "1000000000000000000": {
      "text": "[X-FS] 📌 ROOT / SUPERBLOCK #x_fs_root",
      "in_reply_to": null
    },
    "1000000000000000001": {
      "text": "[X-FS] 📁 mkdir photos #x_fs_dir",
      "in_reply_to": "1000000000000000000"
    },
    "1000000000000000002": {
      "text": "[X-FS] 📄 touch test.txt #x_fs_file",
      "in_reply_to": "1000000000000000000"
    },
    "1000000000000000003": {
      "text": "[X-FS_CHUNK:0] aGVsbG8gWC1GUwo=",
      "in_reply_to": "1000000000000000002"
    }
  }
}

本物のXのスレッドそっくり で、地味にツボります。あなたの hello X-FS という11バイトのテキストが、3つのXポストとして永遠に残るかもしれない世界。


第6章: なぜこんなものを作ったのか

ここまで読んでくださった方は、薄々お気づきだと思います。

完全に実用性はありません。

でも、こういうものを作る過程で得られるものは、意外と多いんですよね。

学べたこと

  • FUSEの基本: getattr, readdir, read, write, create, unlink などの実装
  • ファイルシステムの内部構造: スーパーブロック、inode、データブロックの関係
  • チャンク分割と再構成: 制約のあるストレージへの適応
  • AIによる設計レビュー: 今回の実装はClaude (Anthropic) と何度も対話しながら詰めました

そして何より、

「全てのサービスはファイルシステムになりたがる」を体感できる

UNIX哲学に /proc, /sys のような「あらゆるものをファイルとして抽象化する」思想があります。GmailFSはその思想をサービス越しにやった先駆者でした。

そして20年後、Xでも同じことができることが(理論上)証明されました。SlackFS, NotionFS, DiscordFS だってきっと作れます。実用性?知らない子ですね。

業務にどう活きる?

…活きません。正直に書きます。活きません。

でも、こういう「絶対に役に立たないもの」を本気で作ろうとする時間が、エンジニアの引き出しを広げてくれるんじゃないかな、と思っています。少なくとも私は、この記事を書くために行った計算で「Base64のオーバーヘッドって33%もあるのか」と再認識しました。

業務で似たような制約に当たった時、「あ、これX-FSで考えたやつだ」と思い出せるかもしれません。多分思い出さないですが。


おわりに

GmailFS から22年。

時代は変わり、ストレージは安くなり、APIは有料化され、利用規約は厳しくなりました。それでも「サービスをファイルシステムとして使う」というロマンは、ギークの心に生き続けています。

もし本記事を読んで「俺ならNotionでやる」「私はSlackで作りたい」と思った方がいたら、ぜひ作って共有してください。きっと記事がバズります。たぶん。

最後に、本物のX APIに繋ぐコードは入れていません。「凍結覚悟で挑む勇者」のために、拡張ポイントだけは残してあります。墓碑銘も書きます。

それでは、良いハック生活を。


参考リンク

追記: 想定問答

Q. これ本当に動くんですか?
A. モックバックエンドでは動きます。本物のX APIに繋ぐと凍結するまでは動きます

Q. 規約違反では?
A. ローカルJSONをバックエンドにする限り、X社には1バイトも送信されません。規約違反するためには、追加実装+勇気が必要です。

Q. なぜ「1.4億倍」だったタイトルなのに、本文で300億倍と書いたのか?
A. タイトルを書いた時点では雑な見積もりでした。本気で計算したら更にひどかったというオチです。タイトル詐欺で申し訳ない。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?