mirror of
https://github.com/silvanocerza/github-gitless-sync.git
synced 2026-07-22 12:10:28 +00:00
Add benchmark script
This commit is contained in:
parent
f2452b1c60
commit
035f223aef
1 changed files with 232 additions and 0 deletions
232
benchmark.ts
Normal file
232
benchmark.ts
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
import * as fs from "fs";
|
||||
import * as proxyquire from "proxyquire";
|
||||
import * as obsidianMocks from "./mock-obsidian";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import * as crypto from "crypto";
|
||||
const proxyquireNonStrict = proxyquire.noCallThru();
|
||||
|
||||
const LoggerModule = proxyquireNonStrict("./src/logger", {
|
||||
obsidian: obsidianMocks,
|
||||
});
|
||||
|
||||
const GithubClientModule = proxyquireNonStrict("./src/github/client", {
|
||||
obsidian: obsidianMocks,
|
||||
});
|
||||
|
||||
const MetadataStoreModule = proxyquireNonStrict("./src/metadata-store", {
|
||||
obsidian: obsidianMocks,
|
||||
});
|
||||
|
||||
const EventsListenerModule = proxyquireNonStrict("./src/events-listener", {
|
||||
obsidian: obsidianMocks,
|
||||
"./metadata-store": MetadataStoreModule,
|
||||
});
|
||||
|
||||
const UtilsModule = proxyquireNonStrict("./src/utils", {
|
||||
obsidian: obsidianMocks,
|
||||
});
|
||||
|
||||
const SyncManagerModule = proxyquireNonStrict("./src/sync-manager", {
|
||||
obsidian: obsidianMocks,
|
||||
"./metadata-store": MetadataStoreModule,
|
||||
"./events-listener": EventsListenerModule,
|
||||
"./github/client": GithubClientModule,
|
||||
"./utils": UtilsModule,
|
||||
});
|
||||
|
||||
async function runBenchmark(vaultRootDir: string) {
|
||||
const vault = new obsidianMocks.Vault(vaultRootDir);
|
||||
console.log("Vault config dir:", vault.configDir);
|
||||
|
||||
// Create a real logger with our mock vault
|
||||
const logger = new LoggerModule.default(vault, false);
|
||||
|
||||
// Settings for the sync manager
|
||||
const settings = {
|
||||
githubToken: process.env.GITHUB_TOKEN,
|
||||
githubOwner: process.env.REPO_OWNER,
|
||||
githubRepo: process.env.REPO_NAME,
|
||||
githubBranch: process.env.REPO_BRANCH,
|
||||
syncConfigDir: false,
|
||||
};
|
||||
|
||||
// We're not going to get any conflicts, this is useless
|
||||
const onConflicts = async () => {
|
||||
return [];
|
||||
};
|
||||
|
||||
// Create the sync manager
|
||||
const SyncManager = SyncManagerModule.default;
|
||||
const syncManager = new SyncManager(vault, settings, onConflicts, logger);
|
||||
await syncManager.loadMetadata();
|
||||
|
||||
const startTime = performance.now();
|
||||
await syncManager.firstSync();
|
||||
return performance.now() - startTime;
|
||||
}
|
||||
|
||||
const generateRandomFiles = (
|
||||
rootPath: string,
|
||||
numFiles: number,
|
||||
maxDepth: number,
|
||||
maxTotalSize: number,
|
||||
) => {
|
||||
// Create root directory if it doesn't exist
|
||||
if (!fs.existsSync(rootPath)) {
|
||||
fs.mkdirSync(rootPath, { recursive: true });
|
||||
}
|
||||
|
||||
// Generate folder structure first
|
||||
const allFolderPaths = [rootPath];
|
||||
|
||||
for (let depth = 1; depth <= maxDepth; depth++) {
|
||||
const numFoldersAtThisDepth = Math.floor(Math.random() * 3) + 1; // 1-3 folders per level
|
||||
|
||||
for (let i = 0; i < numFoldersAtThisDepth; i++) {
|
||||
const parentPath =
|
||||
allFolderPaths[Math.floor(Math.random() * allFolderPaths.length)];
|
||||
const currentDepthOfParent =
|
||||
parentPath.split(path.sep).length - rootPath.split(path.sep).length;
|
||||
|
||||
// Only create subfolders if we haven't reached max depth for this path
|
||||
if (currentDepthOfParent < maxDepth) {
|
||||
const folderName = crypto.randomBytes(5).toString("hex");
|
||||
const newFolderPath = path.join(parentPath, folderName);
|
||||
|
||||
fs.mkdirSync(newFolderPath, { recursive: true });
|
||||
allFolderPaths.push(newFolderPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now generate files
|
||||
let totalSize = 0;
|
||||
// Calculate approximate size per file to ensure we create all files
|
||||
const targetSizePerFile = Math.floor(maxTotalSize / numFiles);
|
||||
|
||||
for (let i = 0; i < numFiles; i++) {
|
||||
// Pick a random folder to place the file in
|
||||
const targetFolder =
|
||||
allFolderPaths[Math.floor(Math.random() * allFolderPaths.length)];
|
||||
|
||||
// Generate random file name
|
||||
const fileName = crypto.randomBytes(8).toString("hex") + ".md";
|
||||
const filePath = path.join(targetFolder, fileName);
|
||||
|
||||
// Calculate remaining size and files
|
||||
const remainingSize = maxTotalSize - totalSize;
|
||||
const remainingFiles = numFiles - i;
|
||||
|
||||
if (remainingSize <= 0) {
|
||||
// If we've hit the max size but still need files, create empty files
|
||||
fs.writeFileSync(filePath, "");
|
||||
} else {
|
||||
// Calculate appropriate file size to ensure we can create all files
|
||||
const maxPossibleSize = Math.floor(remainingSize / remainingFiles);
|
||||
const contentSize =
|
||||
Math.floor(
|
||||
Math.random() * Math.min(maxPossibleSize, targetSizePerFile),
|
||||
) + 1;
|
||||
|
||||
// Generate random content
|
||||
const content = crypto.randomBytes(contentSize).toString("hex");
|
||||
|
||||
// Write file
|
||||
fs.writeFileSync(filePath, content);
|
||||
totalSize += contentSize;
|
||||
}
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
};
|
||||
|
||||
const cleanupRemote = async () => {
|
||||
const owner = process.env.REPO_OWNER;
|
||||
const repo = process.env.REPO_NAME;
|
||||
const branch = process.env.REPO_BRANCH;
|
||||
const headers = {
|
||||
Accept: "application/vnd.github+json",
|
||||
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
};
|
||||
try {
|
||||
// Get the manifest SHA
|
||||
const contentResponse = await obsidianMocks.requestUrl({
|
||||
url: `https://api.github.com/repos/${owner}/${repo}/contents/.obsidian/github-sync-metadata.json?ref=${branch}`,
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
// Delete manifest
|
||||
await obsidianMocks.requestUrl({
|
||||
url: `https://api.github.com/repos/${owner}/${repo}/contents/.obsidian/github-sync-metadata.json`,
|
||||
method: "DELETE",
|
||||
headers: headers,
|
||||
body: JSON.stringify({
|
||||
message: "Cleanup",
|
||||
sha: contentResponse.json.sha,
|
||||
branch: branch,
|
||||
}),
|
||||
});
|
||||
console.log("Remote repository cleaned up successfully");
|
||||
} catch (error) {
|
||||
console.error("Failed to clean up remote repository:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const BENCHMARK_DATA = [
|
||||
{
|
||||
files: 1,
|
||||
maxDepth: 1,
|
||||
maxSize: 7000,
|
||||
},
|
||||
];
|
||||
|
||||
(async () => {
|
||||
const tmp = os.tmpdir();
|
||||
const benchmarkRootDir = path.join(tmp, "github-gitless-sync-benchmark");
|
||||
try {
|
||||
const results = [];
|
||||
for (const data of BENCHMARK_DATA) {
|
||||
const vaultRootDir = path.join(
|
||||
benchmarkRootDir,
|
||||
`${data.files}-${data.maxDepth}-${data.maxSize}`,
|
||||
);
|
||||
// Generates random files
|
||||
generateRandomFiles(
|
||||
vaultRootDir,
|
||||
data.files,
|
||||
data.maxDepth,
|
||||
data.maxSize,
|
||||
);
|
||||
|
||||
// Run first sync by uploading all local files
|
||||
const uploadTime = await runBenchmark(vaultRootDir);
|
||||
|
||||
// Cleanup vault dir completely
|
||||
fs.rmSync(vaultRootDir, { recursive: true, force: true });
|
||||
|
||||
// Run first sync again, this time we download the files we just uploaded
|
||||
const downloadTime = await runBenchmark(vaultRootDir);
|
||||
|
||||
// Cleanup the remote repo so it's ready for another benchmark
|
||||
await cleanupRemote();
|
||||
|
||||
results.push({
|
||||
data,
|
||||
uploadTime,
|
||||
downloadTime,
|
||||
});
|
||||
}
|
||||
fs.writeFileSync("benchmark_result.json", JSON.stringify(results), {
|
||||
flag: "w",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Benchmark failed:", error);
|
||||
}
|
||||
fs.rm(benchmarkRootDir, { recursive: true, force: true }, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
})();
|
||||
Loading…
Reference in a new issue