MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能: - ✅ Categories/Series双视图管理(category_view.rs + import_markdown.rs) - ✅ FUSE Multi-Volume支持(tree_type参数) - ✅ SSH/SFTP/SCP/rsync协议完整实现(4042行) - ✅ NFS/SMB Module Phase 1-3完成 - ✅ Archive Module Phase 1-4完成(2916行) - ✅ Download Center API完整实现 - ✅ S3兼容API实现(560行) Git配置修正: - ✅ 删除错误origin(gitea.momentry.ddns.net) - ✅ 删除m5max128(指向机器名) - ✅ 设置origin = m5max128gitea.momentry.ddns.net/admin/markbase - ✅ 设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase 数据清理: - ✅ 删除38个临时SQLite(保留accusys.sqlite、demo.sqlite) - ✅ 删除.bak、test_*.bin、调试脚本等临时文件 - ✅ 删除临时目录(build/、download files/、raid_test/等) - ✅ 更新.gitignore排除临时文件 架构优化: - 52个文件修改,2434行新增,4739行删除 - Workspace成员整合(16个crate) - 数据库状态:accusys.sqlite保留(主demo测试) 远程同步: - ✅ 准备推送到m5max128gitea(远程Gitea) - ✅ 准备推送到m4minigitea(本地Gitea)
This commit is contained in:
460
MarkBaseFS/MarkBaseFS/FileLevelStorage.swift
Normal file
460
MarkBaseFS/MarkBaseFS/FileLevelStorage.swift
Normal file
@@ -0,0 +1,460 @@
|
||||
import Foundation
|
||||
|
||||
public class FileLevelStorage {
|
||||
|
||||
// File Level Storage for MarkBaseFS
|
||||
// No DriverKit Entitlement required
|
||||
// Uses FileManager API for all storage operations
|
||||
|
||||
private let frameIndexTable: FrameIndexTable
|
||||
private let fileManager = FileManager.default
|
||||
|
||||
// Multi-tier storage paths
|
||||
private var nvmeTierPath: String = "/Volumes/MarkBaseFS_Test" // vdisk for POC
|
||||
private var hddTierPath: String = "/Volumes/HDD_RAID"
|
||||
private var objectStorageEndpoint: String = "http://localhost:9000"
|
||||
|
||||
// Object Storage Client
|
||||
private var objectStorageClient: ObjectStorageClient?
|
||||
|
||||
// Performance tracking
|
||||
private var writeSpeedMBps: Double = 0
|
||||
private var readSpeedMBps: Double = 0
|
||||
|
||||
public init(frameIndexTable: FrameIndexTable) {
|
||||
self.frameIndexTable = frameIndexTable
|
||||
print("FileLevelStorage initializing...")
|
||||
print(" - NVMe Tier (vdisk): \(nvmeTierPath)")
|
||||
print(" - HDD Tier: \(hddTierPath)")
|
||||
print(" - Object Storage: \(objectStorageEndpoint)")
|
||||
|
||||
// Initialize Object Storage Client
|
||||
initializeObjectStorageClient()
|
||||
}
|
||||
|
||||
private func initializeObjectStorageClient() {
|
||||
let config = ObjectStorageConfig.minIODefault()
|
||||
objectStorageClient = ObjectStorageClient(
|
||||
endpoint: config.endpoint,
|
||||
accessKey: config.accessKey,
|
||||
secretKey: config.secretKey
|
||||
)
|
||||
|
||||
print(" - Object Storage Client initialized")
|
||||
}
|
||||
|
||||
// MARK: - vdisk Access Test
|
||||
|
||||
public func testVDiskAccess() {
|
||||
print("\n=== FileLevelStorage: Multi-tier Storage Test ===")
|
||||
|
||||
// Test NVMe tier (vdisk)
|
||||
testNVMeTier()
|
||||
|
||||
// Test HDD tier
|
||||
testHDDTier()
|
||||
|
||||
// Test Object Storage tier
|
||||
testObjectStorageTier()
|
||||
|
||||
// Test Multi-tier integration
|
||||
testMultiTierIntegration()
|
||||
|
||||
print("\n=== Multi-tier Storage Test Complete ===")
|
||||
}
|
||||
|
||||
private func testNVMeTier() {
|
||||
print("\nTest: NVMe Tier (vdisk)")
|
||||
testVDiskMount()
|
||||
testFileOperations()
|
||||
testFrameStorage()
|
||||
testPerformance()
|
||||
}
|
||||
|
||||
private func testHDDTier() {
|
||||
print("\nTest: HDD Tier")
|
||||
|
||||
print(" - Checking HDD tier mount point: \(hddTierPath)")
|
||||
|
||||
if fileManager.fileExists(atPath: hddTierPath) {
|
||||
print(" Result: ✅ SUCCESS - HDD tier mounted")
|
||||
|
||||
do {
|
||||
let attributes = try fileManager.attributesOfFileSystem(forPath: hddTierPath)
|
||||
|
||||
if let totalSize = attributes[.systemSize] as? UInt64 {
|
||||
let sizeGB = Double(totalSize) / (1024 * 1024 * 1024)
|
||||
print(" Total Size: \(String(format: "%.2f", sizeGB)) GB")
|
||||
}
|
||||
} catch {
|
||||
print(" ⚠️ Warning: Could not get file system attributes: \(error)")
|
||||
}
|
||||
} else {
|
||||
print(" Result: ⚠️ WARNING - HDD tier not mounted")
|
||||
print(" Note: HDD tier not available for POC testing")
|
||||
}
|
||||
}
|
||||
|
||||
private func testObjectStorageTier() {
|
||||
print("\nTest: Object Storage Tier")
|
||||
|
||||
if let client = objectStorageClient {
|
||||
client.testObjectOperations()
|
||||
} else {
|
||||
print(" Result: ⚠️ WARNING - Object Storage Client not initialized")
|
||||
}
|
||||
}
|
||||
|
||||
private func checkObjectStorageAvailable() -> Bool {
|
||||
guard let client = objectStorageClient else {
|
||||
return false
|
||||
}
|
||||
return client.testConnection()
|
||||
}
|
||||
|
||||
private func testMultiTierIntegration() {
|
||||
print("\nTest: Multi-tier Integration")
|
||||
|
||||
// Test tier selection logic
|
||||
print(" - Testing tier selection logic...")
|
||||
|
||||
let videoId = "test_video_multi_tier"
|
||||
let frameNumber: UInt64 = 0
|
||||
|
||||
// NVMe tier: hot frames (recently accessed)
|
||||
print(" Tier for hot frame: \(getStorageTier(for: videoId, frameNumber: frameNumber, accessPattern: .hot))")
|
||||
|
||||
// HDD tier: cold frames (infrequently accessed)
|
||||
print(" Tier for cold frame: \(getStorageTier(for: videoId, frameNumber: frameNumber, accessPattern: .cold))")
|
||||
|
||||
// Object Storage tier: archive frames (long-term storage)
|
||||
print(" Tier for archive frame: \(getStorageTier(for: videoId, frameNumber: frameNumber, accessPattern: .archive))")
|
||||
|
||||
print(" - Multi-tier integration: ✅ SUCCESS")
|
||||
}
|
||||
|
||||
private func testVDiskMount() {
|
||||
print("Test 1: vdisk Mount Verification")
|
||||
|
||||
print(" - Checking vdisk mount point: \(nvmeTierPath)")
|
||||
|
||||
if fileManager.fileExists(atPath: nvmeTierPath) {
|
||||
print(" Result: ✅ SUCCESS - vdisk mounted")
|
||||
|
||||
do {
|
||||
let attributes = try fileManager.attributesOfFileSystem(forPath: nvmeTierPath)
|
||||
|
||||
if let totalSize = attributes[.systemSize] as? UInt64 {
|
||||
let sizeGB = Double(totalSize) / (1024 * 1024 * 1024)
|
||||
print(" Total Size: \(String(format: "%.2f", sizeGB)) GB")
|
||||
}
|
||||
|
||||
if let freeSize = attributes[.systemFreeSize] as? UInt64 {
|
||||
let freeGB = Double(freeSize) / (1024 * 1024 * 1024)
|
||||
print(" Free Size: \(String(format: "%.2f", freeGB)) GB")
|
||||
}
|
||||
} catch {
|
||||
print(" ⚠️ Warning: Could not get file system attributes: \(error)")
|
||||
}
|
||||
} else {
|
||||
print(" Result: ❌ FAILED - vdisk not mounted")
|
||||
}
|
||||
}
|
||||
|
||||
private func testFileOperations() {
|
||||
print("Test 2: File Operations")
|
||||
|
||||
let testFilePath = nvmeTierPath + "/markbase_test.txt"
|
||||
let testContent = "MarkBaseFS File Level Storage Test\n".data(using: .utf8)!
|
||||
|
||||
// Write test
|
||||
print(" - Write test")
|
||||
do {
|
||||
fileManager.createFile(atPath: testFilePath, contents: testContent, attributes: nil)
|
||||
print(" Result: ✅ SUCCESS - File created")
|
||||
} catch {
|
||||
print(" Result: ❌ FAILED - \(error)")
|
||||
}
|
||||
|
||||
// Read test
|
||||
print(" - Read test")
|
||||
do {
|
||||
let readContent = fileManager.contents(atPath: testFilePath)
|
||||
if readContent == testContent {
|
||||
print(" Result: ✅ SUCCESS - File read correctly")
|
||||
} else {
|
||||
print(" Result: ❌ FAILED - Content mismatch")
|
||||
}
|
||||
} catch {
|
||||
print(" Result: ❌ FAILED - \(error)")
|
||||
}
|
||||
|
||||
// Delete test
|
||||
print(" - Delete test")
|
||||
do {
|
||||
try fileManager.removeItem(atPath: testFilePath)
|
||||
print(" Result: ✅ SUCCESS - File deleted")
|
||||
} catch {
|
||||
print(" Result: ❌ FAILED - \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func testFrameStorage() {
|
||||
print("Test 3: Frame Storage Integration")
|
||||
|
||||
// Create test frames
|
||||
let testFrames = createTestFrames(count: 10)
|
||||
|
||||
print(" - Created \(testFrames.count) test frames")
|
||||
|
||||
// Insert frames to Frame Index Table
|
||||
print(" - Inserting frames to Frame Index Table...")
|
||||
|
||||
for (index, frame) in testFrames.enumerated() {
|
||||
let videoId = "test_video_\(index)"
|
||||
let frameIndex = index
|
||||
let frameFile = nvmeTierPath + "/frame_\(index).bin"
|
||||
let frameOffset: Int = 0
|
||||
let frameSize: Int = 1024
|
||||
let frameChecksum = "test_checksum_\(index)"
|
||||
|
||||
frameIndexTable.insertFrame(
|
||||
frameId: UUID().uuidString,
|
||||
videoId: videoId,
|
||||
frameIndex: frameIndex,
|
||||
frameFile: frameFile,
|
||||
frameOffset: frameOffset,
|
||||
frameSize: frameSize,
|
||||
frameChecksum: frameChecksum
|
||||
)
|
||||
|
||||
print(" Inserted frame \(index): \(frameFile)")
|
||||
}
|
||||
|
||||
// Verify frames
|
||||
print(" - Verifying frames in Frame Index Table...")
|
||||
|
||||
let retrievedFrames = frameIndexTable.getFramesForVideo(videoId: "test_video_0")
|
||||
|
||||
if retrievedFrames.count == 10 {
|
||||
print(" Result: ✅ SUCCESS - All frames retrieved")
|
||||
} else {
|
||||
print(" Result: ❌ FAILED - Expected 10 frames, got \(retrievedFrames.count)")
|
||||
}
|
||||
}
|
||||
|
||||
private func testPerformance() {
|
||||
print("Test 4: Performance Test")
|
||||
|
||||
let performanceFilePath = nvmeTierPath + "/performance_test.bin"
|
||||
|
||||
// Write performance test
|
||||
print(" - Write performance test")
|
||||
let writeData = Data(count: 1024 * 1024 * 100) // 100 MB
|
||||
|
||||
let writeStart = Date()
|
||||
do {
|
||||
fileManager.createFile(atPath: performanceFilePath, contents: writeData, attributes: nil)
|
||||
let writeEnd = Date()
|
||||
let writeDuration = writeEnd.timeIntervalSince(writeStart)
|
||||
writeSpeedMBps = 100.0 / writeDuration
|
||||
|
||||
print(" Write Speed: \(String(format: "%.2f", writeSpeedMBps)) MB/s")
|
||||
print(" Result: ✅ SUCCESS")
|
||||
} catch {
|
||||
print(" Result: ❌ FAILED - \(error)")
|
||||
}
|
||||
|
||||
// Read performance test
|
||||
print(" - Read performance test")
|
||||
let readStart = Date()
|
||||
do {
|
||||
let readData = fileManager.contents(atPath: performanceFilePath)
|
||||
let readEnd = Date()
|
||||
let readDuration = readEnd.timeIntervalSince(readStart)
|
||||
let dataSizeMB = Double(readData?.count ?? 0) / (1024 * 1024)
|
||||
readSpeedMBps = dataSizeMB / readDuration
|
||||
|
||||
print(" Read Speed: \(String(format: "%.2f", readSpeedMBps)) MB/s")
|
||||
print(" Result: ✅ SUCCESS")
|
||||
} catch {
|
||||
print(" Result: ❌ FAILED - \(error)")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
do {
|
||||
try fileManager.removeItem(atPath: performanceFilePath)
|
||||
} catch {
|
||||
print(" ⚠️ Warning: Could not cleanup performance test file")
|
||||
}
|
||||
|
||||
// Performance summary
|
||||
print(" Performance Summary:")
|
||||
print(" Write Speed: \(String(format: "%.2f", writeSpeedMBps)) MB/s")
|
||||
print(" Read Speed: \(String(format: "%.2f", readSpeedMBps)) MB/s")
|
||||
|
||||
if writeSpeedMBps > 100 && readSpeedMBps > 100 {
|
||||
print(" ✅ Performance meets POC requirements")
|
||||
} else {
|
||||
print(" ⚠️ Performance below POC requirements")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Frame Operations
|
||||
|
||||
public func storeFrame(videoId: String, frameNumber: UInt64, data: Data) -> Bool {
|
||||
let filePath = nvmeTierPath + "/videos/\(videoId)/frame_\(frameNumber).bin"
|
||||
|
||||
// Create directory if needed
|
||||
let dirPath = nvmeTierPath + "/videos/\(videoId)"
|
||||
do {
|
||||
try fileManager.createDirectory(atPath: dirPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
print("Error creating directory: \(error)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Write frame data
|
||||
do {
|
||||
fileManager.createFile(atPath: filePath, contents: data, attributes: nil)
|
||||
|
||||
// Insert to Frame Index Table
|
||||
frameIndexTable.insertFrame(
|
||||
frameId: UUID().uuidString,
|
||||
videoId: videoId,
|
||||
frameIndex: Int(frameNumber),
|
||||
frameFile: filePath,
|
||||
frameOffset: 0,
|
||||
frameSize: data.count,
|
||||
frameChecksum: "stored_checksum"
|
||||
)
|
||||
|
||||
return true
|
||||
} catch {
|
||||
print("Error storing frame: \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func retrieveFrame(videoId: String, frameNumber: UInt64) -> Data? {
|
||||
// Get frame from Frame Index Table
|
||||
let frames = frameIndexTable.getFramesForVideo(videoId: videoId)
|
||||
|
||||
for frame in frames {
|
||||
if let frameIndex = frame["frame_index"] as? Int, frameIndex == Int(frameNumber) {
|
||||
// Read frame data from file
|
||||
if let frameFile = frame["frame_file"] as? String {
|
||||
do {
|
||||
let data = fileManager.contents(atPath: frameFile)
|
||||
return data
|
||||
} catch {
|
||||
print("Error retrieving frame: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public func deleteFrame(videoId: String, frameNumber: UInt64) -> Bool {
|
||||
// Get frame from Frame Index Table
|
||||
let frames = frameIndexTable.getFramesForVideo(videoId: videoId)
|
||||
|
||||
for frame in frames {
|
||||
if let frameIndex = frame["frame_index"] as? Int, frameIndex == Int(frameNumber) {
|
||||
// Delete frame file
|
||||
if let frameFile = frame["frame_file"] as? String, let frameId = frame["frame_id"] as? String {
|
||||
do {
|
||||
try fileManager.removeItem(atPath: frameFile)
|
||||
|
||||
// Delete from Frame Index Table
|
||||
frameIndexTable.deleteFrame(frameId: frameId)
|
||||
|
||||
return true
|
||||
} catch {
|
||||
print("Error deleting frame: \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: - Multi-tier Storage
|
||||
|
||||
public enum AccessPattern {
|
||||
case hot // Recently accessed, high performance required
|
||||
case cold // Infrequently accessed, moderate performance
|
||||
case archive // Long-term storage, low performance acceptable
|
||||
}
|
||||
|
||||
public func getStorageTier(for videoId: String, frameNumber: UInt64, accessPattern: AccessPattern = .hot) -> StorageTier {
|
||||
// Determine storage tier based on access pattern
|
||||
switch accessPattern {
|
||||
case .hot:
|
||||
return .nvme // NVMe tier for hot frames
|
||||
case .cold:
|
||||
return .hdd // HDD tier for cold frames
|
||||
case .archive:
|
||||
return .objectStorage // Object Storage tier for archive frames
|
||||
}
|
||||
}
|
||||
|
||||
public func getStorageTier(for videoId: String) -> StorageTier {
|
||||
// Legacy function for compatibility
|
||||
return getStorageTier(for: videoId, frameNumber: 0, accessPattern: .hot)
|
||||
}
|
||||
|
||||
public func migrateToTier(videoId: String, targetTier: StorageTier) -> Bool {
|
||||
// Migrate video data to target tier
|
||||
// For POC, migration is not implemented
|
||||
|
||||
print("Migration to \(targetTier) not implemented for POC")
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: - Helper Functions
|
||||
|
||||
private func createTestFrames(count: Int) -> [Data] {
|
||||
var frames: [Data] = []
|
||||
|
||||
for i in 0..<count {
|
||||
let frameData = Data(count: 1024) // 1 KB test frame
|
||||
frames.append(frameData)
|
||||
}
|
||||
|
||||
return frames
|
||||
}
|
||||
|
||||
public func getStorageStats() -> StorageStats {
|
||||
let objectStorageAvailable = checkObjectStorageAvailable()
|
||||
|
||||
return StorageStats(
|
||||
writeSpeedMBps: writeSpeedMBps,
|
||||
readSpeedMBps: readSpeedMBps,
|
||||
nvmeTierAvailable: fileManager.fileExists(atPath: nvmeTierPath),
|
||||
hddTierAvailable: fileManager.fileExists(atPath: hddTierPath),
|
||||
objectStorageAvailable: objectStorageAvailable
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Supporting Types
|
||||
|
||||
public enum StorageTier {
|
||||
case nvme
|
||||
case hdd
|
||||
case objectStorage
|
||||
}
|
||||
|
||||
public struct StorageStats {
|
||||
let writeSpeedMBps: Double
|
||||
let readSpeedMBps: Double
|
||||
let nvmeTierAvailable: Bool
|
||||
let hddTierAvailable: Bool
|
||||
let objectStorageAvailable: Bool
|
||||
}
|
||||
Reference in New Issue
Block a user