Skip to content

Commit

Permalink
Enhance DNS Prefetch - 1st Phase
Browse files Browse the repository at this point in the history
  • Loading branch information
zx8086 committed Feb 2, 2025
1 parent 75d304f commit a28d2b6
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 42 deletions.
20 changes: 20 additions & 0 deletions hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import type { Handle } from '@sveltejs/kit';
import { redirect } from '@sveltejs/kit';
import { dns } from "bun";

// Define paths that should be public
const PUBLIC_PATHS = [
Expand Down Expand Up @@ -53,5 +54,24 @@ export const handle: Handle = async ({ event, resolve }) => {
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');

const prefetchAuthenticatedEndpoints = () => {
const endpoints = [
new URL(Bun.env.GRAPHQL_ENDPOINT || '').hostname,
new URL(Bun.env.API_BASE_URL || '').hostname
];

endpoints.forEach(hostname => {
try {
dns.prefetch(hostname);
} catch (error) {
console.warn(`Auth endpoints DNS prefetch failed for ${hostname}:`, error);
}
});
};

if (authCookie) {
prefetchAuthenticatedEndpoints();
}

return response;
};
4 changes: 4 additions & 0 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { Handle } from '@sveltejs/kit';
import { prefetchDnsForCategories } from "$lib/config/dnsConfig";

export const handle: Handle = async ({ event, resolve }) => {
// Prefetch DNS for CDN and API endpoints on startup
await prefetchDnsForCategories(['cdn', 'api']);

const response = await resolve(event);

// Add security headers (excluding CSP which is handled by svelte.config.js)
Expand Down
69 changes: 69 additions & 0 deletions src/lib/config/dnsConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { backendConfig } from "$backendConfig";
import { frontendConfig } from "$frontendConfig";
import { safeDnsPrefetch } from "$lib/utils/dnsUtils";

// Helper to safely extract hostname from URL string
const getHostname = (url: string): string | null => {
try {
return new URL(url).hostname;
} catch {
return null;
}
};

// Define DNS prefetch targets by category
export const dnsPrefetchTargets = {
// API Endpoints
api: [
getHostname(backendConfig.application.GRAPHQL_ENDPOINT),
getHostname(backendConfig.capella.API_BASE_URL),
'api.openai.com',
'api.pinecone.io'
],

// Monitoring & APM
monitoring: [
getHostname(frontendConfig.elasticApm.SERVER_URL),
getHostname(frontendConfig.openreplay.INGEST_POINT),
getHostname(backendConfig.openTelemetry.TRACES_ENDPOINT),
getHostname(backendConfig.openTelemetry.METRICS_ENDPOINT),
getHostname(backendConfig.openTelemetry.LOGS_ENDPOINT)
],

// Authentication
auth: [
'login.microsoftonline.com'
],

// Content Delivery
cdn: [
'd2bgp0ri487o97.cloudfront.net'
]
} as const;

// Helper to get all unique, valid hostnames for a given category
export function getDnsPrefetchTargets(categories: (keyof typeof dnsPrefetchTargets)[] = Object.keys(dnsPrefetchTargets) as any): string[] {
const hostnames = categories
.flatMap(category => dnsPrefetchTargets[category])
.filter((hostname): hostname is string =>
hostname !== null &&
hostname !== undefined &&
hostname !== ''
);

// Remove duplicates and localhost
return [...new Set(hostnames)]
.filter(hostname =>
!hostname.includes('localhost') &&
!hostname.includes('127.0.0.1')
);
}

// Helper to prefetch DNS for specific categories
export async function prefetchDnsForCategories(categories: (keyof typeof dnsPrefetchTargets)[]): Promise<void> {
const targets = getDnsPrefetchTargets(categories);
await safeDnsPrefetch(targets);
}

// Export the type for use in other files
export type DnsPrefetchCategory = keyof typeof dnsPrefetchTargets;
44 changes: 12 additions & 32 deletions src/lib/context/tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,60 +115,40 @@ export async function initTracker() {
setupAPMIntegration(trackerInstance);
}

// Add tracker assist plugin with enhanced user identification
// Update Assist plugin configuration with more robust options
trackerInstance.use(trackerAssist({
callConfirm: "Would you like to start a support call?",
controlConfirm: "Would you like to allow support to control your screen?",
onCallStart: () => {
console.log("🎥 Support call started");
toast.info("Support call started", {
toast.success("Support call started", {
description: "You are now connected to a support session",
duration: Infinity
duration: 5000
});
return () => {
console.log("📞 Support call ended");
toast.info("Support call ended", {
description: "Your support session has ended",
duration: Infinity
});
toast.info("Support call ended");
};
},
onRemoteControlStart: () => {
console.log("🖱️ Remote control started");
toast.info("Remote control active", {
toast.warning("Remote control active", {
description: "Support agent now has control of your screen",
duration: Infinity
duration: 5000
});
return () => {
console.log("🔒 Remote control ended");
toast.info("Remote control ended", {
description: "Support agent no longer has control of your screen",
duration: Infinity
});
toast.info("Remote control ended");
};
},
onAgentConnect: (agentInfo: any = {}) => {
const user = get(userAccount);
if (user?.email && trackerInstance) {
// Comprehensive user identification for assist
identifyUser(user.email, {
name: user.name || user.email,
email: user.email,
});
}

const { email = '', name = '', query = '' } = agentInfo;
console.log("👋 Agent connected:", { email, name, query });
toast.info("Support agent connected", {
description: `${name} (${email}) has joined the session`,
duration: Infinity
const { email = '', name = '' } = agentInfo;
console.log("👋 Agent connected:", { email, name });
toast.success(`Support agent ${name} connected`, {
duration: 5000
});
return () => {
console.log("👋 Agent disconnected");
toast.info("Support agent disconnected", {
description: "The support agent has left the session",
duration: Infinity
});
toast.info("Support agent disconnected");
};
}
}));
Expand Down
83 changes: 83 additions & 0 deletions src/lib/utils/dnsUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { log, warn } from "$utils/unifiedLogger";

// Type definition for Bun's DNS cache stats
interface DnsCacheStats {
size: number;
cacheHitsCompleted: number;
cacheHitsInflight: number;
cacheMisses: number;
errors: number;
totalCount: number;
}

let bunDns: {
prefetch: (hostname: string) => void;
getCacheStats: () => DnsCacheStats;
} | null = null;

// Initialize Bun DNS if available
try {
// Using dynamic import to avoid issues in non-Bun environments
if (process.versions?.bun) {
import('bun').then(bun => {
bunDns = bun.dns;
}).catch(err => {
warn('Failed to initialize Bun DNS:', err);
});
}
} catch (error) {
warn('Bun DNS initialization error:', error);
}

// Safe DNS prefetch function that works in both Bun and non-Bun environments
export async function safeDnsPrefetch(hostnames: string[]): Promise<void> {
if (!bunDns) {
log('DNS prefetch skipped - Bun DNS not available');
return;
}

for (const hostname of hostnames) {
try {
bunDns.prefetch(hostname);
log(`DNS prefetch successful for ${hostname}`);
} catch (error) {
warn(`DNS prefetch failed for ${hostname}:`, error);
}
}
}

// Get DNS cache stats safely
export function getDnsCacheStats(): DnsCacheStats | null {
if (!bunDns) {
return null;
}

try {
return bunDns.getCacheStats();
} catch (error) {
warn('Failed to get DNS cache stats:', error);
return null;
}
}

// Log DNS cache effectiveness
export function logDnsCacheEffectiveness(): void {
const stats = getDnsCacheStats();
if (!stats) {
log('DNS cache stats not available');
return;
}

const hitRate = stats.totalCount > 0
? (stats.cacheHitsCompleted / stats.totalCount) * 100
: 0;

log('DNS Cache Effectiveness:', {
hitRate: `${hitRate.toFixed(2)}%`,
hits: stats.cacheHitsCompleted,
misses: stats.cacheMisses,
totalQueries: stats.totalCount,
cacheSize: stats.size,
errors: stats.errors
});
}
15 changes: 11 additions & 4 deletions src/otlp/MonitoredOTLPExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type { ExportResult } from "@opentelemetry/core";
import { backendConfig } from "../backend-config"; // Changed from $backendConfig
import { log, warn, err, debug } from "../utils/browserLogger"; // Changed from $utils/browserLogger
import { otlpConfig } from "./otlpConfig";
import { safeDnsPrefetch, getDnsCacheStats } from "$lib/utils/dnsUtils";
import { getDnsPrefetchTargets } from "$lib/config/dnsConfig";

// Import Bun DNS with type checking
let bunDns: any;
Expand Down Expand Up @@ -102,12 +104,17 @@ export abstract class MonitoredOTLPExporter<T> {

private async initializeDNSPrefetch(): Promise<void> {
try {
const initialStats = bunDns.getCacheStats();
debug("Initial DNS cache stats:", initialStats);
const initialStats = getDnsCacheStats();
if (initialStats) {
debug("Initial DNS cache stats:", initialStats);
}

bunDns.prefetch(this.hostName);
// Add the current hostname to monitoring targets
const targets = [...getDnsPrefetchTargets(['monitoring']), this.hostName];
await safeDnsPrefetch(targets);

this.dnsPrefetchInitiated = true;
debug(`DNS prefetch initiated for ${this.hostName} (port ${this.port})`);
debug(`DNS prefetch initiated for ${targets.join(', ')}`);

await this.verifyDNSPrefetch();
} catch (error) {
Expand Down
12 changes: 6 additions & 6 deletions src/routes/api/health-check/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -98,21 +98,21 @@
</svelte:head>

<div class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold mb-4">Health Check Status</h1>
<h1 class="text-3xl font-bold mb-4">Health Check</h1>

<div class="mb-6">
<p class="text-sm text-gray-600 mt-2 mb-4">
{checkType === "Simple"
? "Simple check tests the SQL Database, Internal API & GraphQL endpoint."
: "Detailed check covering all dependencies."}
</p>
<button
onclick={() => toggleCheckType()}
class="w-48 bg-[#00174f] hover:bg-[#00174f]/80 text-white font-bold py-2 px-4 rounded hover:ring-2 hover:ring-red-500 hover:ring-offset-2 transition-all duration-300"
data-transaction-name={`Switch to ${checkType === "Simple" ? "Detailed" : "Simple"} Check`}
>
Switch to {checkType === "Simple" ? "Detailed" : "Simple"}
</button>
<p class="text-sm text-gray-600 mt-2">
{checkType === "Simple"
? "Simple check tests the SQL Database, Internal API & GraphQL endpoint."
: "Detailed check including all API endpoints."}
</p>
</div>

{#if loading}
Expand Down
10 changes: 10 additions & 0 deletions src/routes/api/health-check/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import {
import type { RequestEvent } from '@sveltejs/kit';
import { OpenAI } from "openai";
import { Pinecone } from "@pinecone-database/pinecone";
import { dns } from "bun";
import { safeDnsPrefetch } from "$lib/utils/dnsUtils";
import { prefetchDnsForCategories } from "$lib/config/dnsConfig";

const INDIVIDUAL_CHECK_TIMEOUT = 15000; // 15 seconds timeout for most checks
const CAPELLA_API_TIMEOUT = 30000; // 30 seconds timeout for Capella API
Expand Down Expand Up @@ -436,6 +439,10 @@ async function checkPineconeEndpoint(fetch: typeof global.fetch): Promise<CheckR
}
}

async function prefetchHealthCheckEndpoints() {
await prefetchDnsForCategories(['api', 'monitoring']);
}

export async function GET({ fetch, url }: RequestEvent) {
try {
log("Health check started", {
Expand Down Expand Up @@ -470,6 +477,9 @@ export async function GET({ fetch, url }: RequestEvent) {
{ name: "OpenTelemetry Traces Endpoint", check: () => checkTracesEndpoint(fetch) },
].sort((a, b) => a.name.localeCompare(b.name));

// Call before health checks
await prefetchHealthCheckEndpoints();

// Run all checks in parallel but handle each independently
await Promise.all((isSimpleCheck ? simpleChecks : detailedChecks)
.map(async ({ name, check }) => {
Expand Down

0 comments on commit a28d2b6

Please sign in to comment.