1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自作オブジェクトストレージ日記.part1

Last updated at Posted at 2024-10-27

はじめに

今回作成するストレージは個人用のものであり、これを利用したことに起こった損害について一切の責任を負いませんのであしからず。

なぜ作るのか

自分のデータを預けるのに他人の作ったソフトウェアだと不都合がありそうだから。あと自分にあったストレージソフトウェアが見つからなかったため

背景

大容量ストレージにおける状況

大容量ストレージを扱う技術はすでにあります。企業などでは昔から大容量ストレージを扱っていたため当然でしょう。しかし個人で使うにあたってそのソリューションを導入するのは高価で現実的ではありません。例としてSANストレージを束ねるスイッチの価格はまともな速度なもので10万円を超える。(Lenovo ThinkSystem DB610S、8 ポート )

私の求めるストレージ要件

ここは人によって異なるでしょうが、私の用途はデータ置き場に近いものなので以下のような要件になりました

  • ストレージ効率が良い
    値段に直結するので当たり前です。HDDはもちろん容量単価の一番安い16TBのものを使用します
  • 待ち時間無しで拡張可能
    毎日保存処理が走るのでRAIDの再構築のように数十時間かかるようではいけません。1年に1回ならともかく、月に1回増設予定なので許容できません。
  • 十分な堅牢性
    家にある最も大きいストレージなのでこれを更にバックアップすることはできません。可能な限り堅牢にしたい

いらない機能

  • 権限管理
    私しか使わないため
  • かっこいいダッシュボード
    私しか使わないため。別にあってもいい
  • 速度
    せいぜい我が家のNICの限界である10Gbpsあればよい
  • ARMアーキテクチャ対応
    MACなどどうでもいい。Intelの拡張命令を使います

MinIOでよくね?...と思ったあなた!MinIOは商用利用で月額課金が必要です!

採用技術

  • イレイジャーコーディング
    ストレージ効率が高く破壊耐性もある
  • HighWayHash
    ビット反転したデータを検出できる

実装編

とりあえずファイルを分割して結合したいと思います。言語はGoを使います。早いからです。

part1/main.go

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	fmt.Println("Hello World!")
	// binary/inputをバイナリファイルとして読み込み、4つに分割してbinary/output_%dに書き込むコード
	inputfile := "binary/input.mp4"
	outputdir := "binary/"

	// inputfileを読み込む
	input, err := os.Open(inputfile)
	if err != nil {
		fmt.Println(err)
		return
	}
	// 関数が終了する際にinputを閉じる
	defer input.Close()

	// inputfileのサイズを取得
	fileInfo, err := input.Stat()
	if err != nil {
		fmt.Println(err)
		return
	}

	fileSize := fileInfo.Size()
	// ファイルサイズを4で割り、4つのファイルに分割する. 4で割り切れなかった余りは最後のファイルに含める
	partSize := fileSize / 4
	remainder := fileSize % 4

	for i := 0; i < 4; i++ {
		// output_%dを作成
		outputFile, err := os.Create(fmt.Sprintf("%soutput_%d", outputdir, i))
		if err != nil {
			fmt.Println(err)
			return
		}
		// 関数が終了する際にoutput_%dを閉じる
		defer outputFile.Close()

		// inputfileからoutput_%dにコピー
		_, err = input.Seek(int64(i)*partSize, 0)
		if err != nil {
			fmt.Println(err)
			return
		}
		// 4つ目のファイルは余りを含む。実際は4や3のような具体的な数字(マジックナンバー)ではなく、変数を使って管理する
		sizeToCopy := partSize
		if i == 3 {
			sizeToCopy += remainder
		}

		// inputからoutput_%dに分割して書き込み
		_, err = io.CopyN(outputFile, input, sizeToCopy)
		if err != nil && err != io.EOF {
			fmt.Println(err)
			return
		}
	}

	// output_0-4を読み込み、それらを結合してmergedを作成するコード
	mergedFile, err := os.Create(outputdir + "merged.mp4")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer mergedFile.Close()

	for i := 0; i < 4; i++ {
		partFile, err := os.Open(outputdir + "output_" + fmt.Sprintf("%d", i))
		if err != nil {
			fmt.Println(err)
			return
		}
		defer partFile.Close()

		_, err = io.Copy(mergedFile, partFile)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
}

#実行結果

part1$ time ./part1 
Hello World!

real    3m0.855s
user    0m0.001s
sys     0m26.098s

さすがにi/oがほとんどなのでreal timeが多いですね。
merged.mp4ファイルは無事再生できました。
タスクマネージャーの雰囲気的に,SSDがボトルネックになっています。今後様々な処理を加えたらどうなるのでしょうか?
image.png
image.png

次やること

データを1MBのブロックに分ける。
part2につづく

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?