Skip to content

Commit

Permalink
fix(store): add missing files PE-2284
Browse files Browse the repository at this point in the history
Missed these in previous commit. Classic git mistake :)
  • Loading branch information
djwhitt committed Oct 19, 2022
1 parent c105a2d commit bb76884
Show file tree
Hide file tree
Showing 4 changed files with 397 additions and 0 deletions.
153 changes: 153 additions & 0 deletions src/store/fs-block-store.ts
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,
});
}
}
}
84 changes: 84 additions & 0 deletions src/store/fs-chunk-data-store.ts
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,
});
}
}
}
82 changes: 82 additions & 0 deletions src/store/fs-chunk-metadata-store.ts
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,
});
}
}
}
Loading

0 comments on commit bb76884

Please sign in to comment.