Implement parallel processing with retry logic for repository mirroring and syncing operations

This commit is contained in:
Arunavo Ray
2025-05-22 13:28:28 +05:30
parent aaf8dc6fe4
commit f4bc28e6c2
6 changed files with 503 additions and 156 deletions

View File

@@ -6,6 +6,8 @@ import { createGitHubClient } from "@/lib/github";
import { mirrorGitHubOrgToGitea } from "@/lib/gitea";
import { repoStatusEnum } from "@/types/Repository";
import { type MembershipRole } from "@/types/organizations";
import { processWithRetry } from "@/lib/utils/concurrency";
import { createMirrorJob } from "@/lib/helpers";
export const POST: APIRoute = async ({ request }) => {
try {
@@ -61,31 +63,73 @@ export const POST: APIRoute = async ({ request }) => {
);
}
// Fire async mirroring without blocking response
// Fire async mirroring without blocking response, using parallel processing
setTimeout(async () => {
for (const org of orgs) {
if (!config.githubConfig.token) {
throw new Error("GitHub token is missing in config.");
}
if (!config.githubConfig.token) {
throw new Error("GitHub token is missing in config.");
}
const octokit = createGitHubClient(config.githubConfig.token);
// Create a single Octokit instance to be reused
const octokit = createGitHubClient(config.githubConfig.token);
try {
// Define the concurrency limit - adjust based on API rate limits
// Using a lower concurrency for organizations since each org might contain many repos
const CONCURRENCY_LIMIT = 2;
// Process organizations in parallel with retry capability
await processWithRetry(
orgs,
async (org) => {
// Prepare organization data
const orgData = {
...org,
status: repoStatusEnum.parse("imported"),
membershipRole: org.membershipRole as MembershipRole,
lastMirrored: org.lastMirrored ?? undefined,
errorMessage: org.errorMessage ?? undefined,
};
// Log the start of mirroring
console.log(`Starting mirror for organization: ${org.name}`);
// Create a mirror job entry to track progress
await createMirrorJob({
userId: config.userId || "",
organizationId: org.id,
organizationName: org.name,
message: `Started mirroring organization: ${org.name}`,
details: `Organization ${org.name} is now in the mirroring queue.`,
status: "mirroring",
});
// Mirror the organization
await mirrorGitHubOrgToGitea({
config,
octokit,
organization: {
...org,
status: repoStatusEnum.parse("imported"),
membershipRole: org.membershipRole as MembershipRole,
lastMirrored: org.lastMirrored ?? undefined,
errorMessage: org.errorMessage ?? undefined,
},
organization: orgData,
});
} catch (error) {
console.error(`Mirror failed for organization ${org.name}:`, error);
return org;
},
{
concurrencyLimit: CONCURRENCY_LIMIT,
maxRetries: 2,
retryDelay: 3000,
onProgress: (completed, total, result) => {
const percentComplete = Math.round((completed / total) * 100);
console.log(`Organization mirroring progress: ${percentComplete}% (${completed}/${total})`);
if (result) {
console.log(`Successfully mirrored organization: ${result.name}`);
}
},
onRetry: (org, error, attempt) => {
console.log(`Retrying organization ${org.name} (attempt ${attempt}): ${error.message}`);
}
}
}
);
console.log("All organization mirroring tasks completed");
}, 0);
const responsePayload: MirrorOrgResponse = {