-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(store): add missing files PE-2284
Missed these in previous commit. Classic git mistake :)
- Loading branch information
Showing
4 changed files
with
397 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
import winston from 'winston'; | ||
|
||
import { jsonBlockToMsgpack, msgpackToJsonBlock } from '../lib/encoding.js'; | ||
import { PartialJsonBlock, PartialJsonBlockStore } from '../types.js'; | ||
|
||
export class FsBlockStore implements PartialJsonBlockStore { | ||
private log: winston.Logger; | ||
private baseDir: string; | ||
|
||
constructor({ log, baseDir }: { log: winston.Logger; baseDir: string }) { | ||
this.log = log.child({ class: this.constructor.name }); | ||
this.baseDir = baseDir; | ||
} | ||
|
||
private blockHashDir(hash: string) { | ||
const blockPrefix = `${hash.substring(0, 2)}/${hash.substring(2, 4)}`; | ||
return `${this.baseDir}/hash/${blockPrefix}`; | ||
} | ||
|
||
private blockHashPath(hash: string) { | ||
return `${this.blockHashDir(hash)}/${hash}.msgpack`; | ||
} | ||
|
||
private blockHeightDir(height: number) { | ||
return `${this.baseDir}/height/${height % 1000}`; | ||
} | ||
|
||
private blockHeightPath(height: number) { | ||
return `${this.blockHeightDir(height)}/${height}.msgpack`; | ||
} | ||
|
||
async hasHash(hash: string) { | ||
try { | ||
await fs.promises.access(this.blockHashPath(hash), fs.constants.F_OK); | ||
return true; | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
async hasHeight(height: number) { | ||
try { | ||
await fs.promises.access(this.blockHeightPath(height), fs.constants.F_OK); | ||
return true; | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
async getByHash(hash: string) { | ||
try { | ||
if (await this.hasHash(hash)) { | ||
const blockData = await fs.promises.readFile(this.blockHashPath(hash)); | ||
return msgpackToJsonBlock(blockData); | ||
} | ||
} catch (error: any) { | ||
this.log.error('Failed to get block by hash', { | ||
hash, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
return undefined; | ||
} | ||
|
||
async getByHeight(height: number) { | ||
try { | ||
if (await this.hasHeight(height)) { | ||
const blockData = await fs.promises.readFile( | ||
this.blockHeightPath(height), | ||
); | ||
return msgpackToJsonBlock(blockData); | ||
} | ||
} catch (error: any) { | ||
this.log.error('Failed to get block by height', { | ||
height, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
return undefined; | ||
} | ||
|
||
async set(block: PartialJsonBlock, height?: number) { | ||
try { | ||
if (!(await this.hasHash(block.indep_hash))) { | ||
await fs.promises.mkdir(this.blockHashDir(block.indep_hash), { | ||
recursive: true, | ||
}); | ||
|
||
const blockData = jsonBlockToMsgpack(block); | ||
await fs.promises.writeFile( | ||
this.blockHashPath(block.indep_hash), | ||
blockData, | ||
); | ||
} | ||
|
||
if (height && !(await this.hasHeight(height))) { | ||
await fs.promises.mkdir(this.blockHeightDir(height), { | ||
recursive: true, | ||
}); | ||
|
||
const targetPath = path.relative( | ||
`${process.cwd()}/${this.blockHeightDir(height)}`, | ||
`${process.cwd()}/${this.blockHashPath(block.indep_hash)}`, | ||
); | ||
await fs.promises.symlink(targetPath, this.blockHeightPath(height)); | ||
} | ||
} catch (error: any) { | ||
this.log.error('Failed to set block', { | ||
hash: block.indep_hash, | ||
height, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
} | ||
|
||
async delByHash(hash: string) { | ||
try { | ||
if (await this.hasHash(hash)) { | ||
await fs.promises.unlink(this.blockHashPath(hash)); | ||
} | ||
} catch (error: any) { | ||
this.log.error('Failed to delete block by hash', { | ||
hash: hash, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
} | ||
|
||
async delByHeight(height: number) { | ||
try { | ||
if (height && !(await this.hasHeight(height))) { | ||
const block = await this.getByHeight(height); | ||
const hash = block?.indep_hash; | ||
if (hash) { | ||
await fs.promises.unlink(this.blockHashPath(hash)); | ||
} | ||
await fs.promises.unlink(this.blockHeightPath(height)); | ||
} | ||
} catch (error: any) { | ||
this.log.error('Failed to delete block by height', { | ||
height: height, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import fs from 'fs'; | ||
import { Readable } from 'stream'; | ||
import winston from 'winston'; | ||
|
||
import { ChunkDataStore } from '../types.js'; | ||
|
||
export class FsChunkDataStore implements ChunkDataStore { | ||
private log: winston.Logger; | ||
private baseDir: string; | ||
|
||
constructor({ log, baseDir }: { log: winston.Logger; baseDir: string }) { | ||
this.log = log.child({ class: this.constructor.name }); | ||
this.baseDir = baseDir; | ||
} | ||
|
||
private chunkDataDir(dataRoot: string) { | ||
return `${this.baseDir}/${dataRoot}/data/`; | ||
} | ||
|
||
private chunkDataPath(dataRoot: string, relativeOffset: number) { | ||
return `${this.chunkDataDir(dataRoot)}/${relativeOffset}`; | ||
} | ||
|
||
async has(dataRoot: string, relativeOffset: number) { | ||
try { | ||
await fs.promises.access( | ||
this.chunkDataPath(dataRoot, relativeOffset), | ||
fs.constants.F_OK, | ||
); | ||
return true; | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
async get( | ||
dataRoot: string, | ||
relativeOffset: number, | ||
): Promise<Buffer | undefined> { | ||
try { | ||
if (await this.has(dataRoot, relativeOffset)) { | ||
return await fs.promises.readFile( | ||
this.chunkDataPath(dataRoot, relativeOffset), | ||
); | ||
} | ||
} catch (error: any) { | ||
this.log.error('Failed to fetch chunk data from cache', { | ||
dataRoot, | ||
relativeOffset, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
async set( | ||
data: Readable, | ||
dataRoot: string, | ||
relativeOffset: number, | ||
): Promise<void> { | ||
try { | ||
await fs.promises.mkdir(this.chunkDataDir(dataRoot), { | ||
recursive: true, | ||
}); | ||
await fs.promises.writeFile( | ||
this.chunkDataPath(dataRoot, relativeOffset), | ||
data, | ||
); | ||
this.log.info('Successfully cached chunk data', { | ||
dataRoot, | ||
relativeOffset, | ||
}); | ||
} catch (error: any) { | ||
this.log.error('Failed to set chunk data in cache:', { | ||
dataRoot, | ||
relativeOffset, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import fs from 'fs'; | ||
import winston from 'winston'; | ||
|
||
import { fromMsgpack, toB64Url, toMsgpack } from '../lib/encoding.js'; | ||
import { ChunkMetadata, ChunkMetadataStore } from '../types.js'; | ||
|
||
export class FsChunkMetadataStore implements ChunkMetadataStore { | ||
private log: winston.Logger; | ||
|
||
constructor({ log }: { log: winston.Logger }) { | ||
this.log = log.child({ class: this.constructor.name }); | ||
} | ||
|
||
private chunkMetadataDir(dataRoot: string) { | ||
return `data/chunks/${dataRoot}/metadata/`; | ||
} | ||
|
||
private chunkMetadataPath(dataRoot: string, relativeOffset: number) { | ||
return `${this.chunkMetadataDir(dataRoot)}/${relativeOffset}`; | ||
} | ||
|
||
async has(dataRoot: string, relativeOffset: number) { | ||
try { | ||
await fs.promises.access( | ||
this.chunkMetadataPath(dataRoot, relativeOffset), | ||
fs.constants.F_OK, | ||
); | ||
return true; | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
async get( | ||
dataRoot: string, | ||
relativeOffset: number, | ||
): Promise<ChunkMetadata | undefined> { | ||
try { | ||
if (await this.has(dataRoot, relativeOffset)) { | ||
const msgpack = await fs.promises.readFile( | ||
this.chunkMetadataPath(dataRoot, relativeOffset), | ||
); | ||
return fromMsgpack(msgpack) as ChunkMetadata; | ||
} | ||
} catch (error: any) { | ||
this.log.error('Failed to fetch chunk data from cache', { | ||
dataRoot, | ||
relativeOffset, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
async set(chunkMetadata: ChunkMetadata): Promise<void> { | ||
const { data_root, offset } = chunkMetadata; | ||
const dataRoot = toB64Url(data_root); | ||
try { | ||
await fs.promises.mkdir(this.chunkMetadataDir(dataRoot), { | ||
recursive: true, | ||
}); | ||
const msgpack = toMsgpack(chunkMetadata); | ||
await fs.promises.writeFile( | ||
this.chunkMetadataPath(toB64Url(data_root), offset), | ||
msgpack, | ||
); | ||
this.log.info('Successfully cached chunk metadata', { | ||
dataRoot, | ||
relativeOffset: offset, | ||
}); | ||
} catch (error: any) { | ||
this.log.error('Failed to set chunk metadata in cache:', { | ||
dataRoot, | ||
relativeOffset: offset, | ||
message: error.message, | ||
stack: error.stack, | ||
}); | ||
} | ||
} | ||
} |
Oops, something went wrong.