feat: dist tracing 4 (#25574)

* feat: distributed tracing 2

* feat: distributed tracing 2

* refactor: feedback

* refactor: feedback

* fix: type error

* fix: trpc error

* feat: distributed tracing - 3

* chore: translation

* refactor: improvements

* fix: feedback

* chore: remove

* feat: dist tracing 4

* fix: type errors

* fix: type errors

* fix: remove string type

* fix: type errors
This commit is contained in:
Udit Takkar
2025-12-15 10:43:22 +00:00
committed by GitHub
parent 0d4108dd69
commit 4e1d05a7e7
15 changed files with 127 additions and 26 deletions
@@ -9,6 +9,7 @@ import {
import { describe, it, beforeEach, vi, expect } from "vitest";
import * as handleConfirmationModule from "@calcom/features/bookings/lib/handleConfirmation";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import { BookingStatus } from "@calcom/prisma/enums";
import { confirmHandler } from "@calcom/trpc/server/routers/viewer/bookings/confirm.handler";
import type { TrpcSessionUser } from "@calcom/trpc/server/types";
@@ -92,6 +93,7 @@ describe("confirmHandler", () => {
timeZone: organizer.timeZone,
username: organizer.username,
} as NonNullable<TrpcSessionUser>,
traceContext: distributedTracing.createTrace("test_confirm_handler"),
};
const res = await confirmHandler({
@@ -10,6 +10,7 @@ import type { z } from "zod";
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
import { handleStripePaymentSuccess } from "@calcom/features/ee/payments/api/webhook";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import { ProfileRepository } from "@calcom/features/profile/repositories/ProfileRepository";
import { weekdayToWeekIndex, type WeekDays } from "@calcom/lib/dayjs";
import type { HttpError } from "@calcom/lib/http-error";
@@ -2287,7 +2288,8 @@ export function getMockedStripePaymentEvent({ paymentIntentId }: { paymentIntent
export async function mockPaymentSuccessWebhookFromStripe({ externalId }: { externalId: string }) {
let webhookResponse = null;
try {
await handleStripePaymentSuccess(getMockedStripePaymentEvent({ paymentIntentId: externalId }));
const traceContext = distributedTracing.createTrace("test_stripe_webhook");
await handleStripePaymentSuccess(getMockedStripePaymentEvent({ paymentIntentId: externalId }), traceContext);
} catch (e) {
log.silly("mockPaymentSuccessWebhookFromStripe:catch", JSON.stringify(e));
webhookResponse = e as HttpError;
@@ -10,13 +10,19 @@ import { getPlatformParams } from "@calcom/features/platform-oauth-client/get-pl
import { PlatformOAuthClientRepository } from "@calcom/features/platform-oauth-client/platform-oauth-client.repository";
import { HttpError as HttpCode } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import type { TraceContext } from "@calcom/lib/tracing";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { BookingStatus } from "@calcom/prisma/enums";
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
const log = logger.getSubLogger({ prefix: ["[handlePaymentSuccess]"] });
export async function handlePaymentSuccess(paymentId: number, bookingId: number) {
export async function handlePaymentSuccess(paymentId: number, bookingId: number, traceContext: TraceContext) {
const updatedTraceContext = distributedTracing.updateTrace(traceContext, {
bookingId,
paymentId,
});
log.debug(`handling payment success for bookingId ${bookingId}`);
const { booking, user: userWithCredentials, evt, eventType } = await getBooking(bookingId);
@@ -87,6 +93,7 @@ export async function handlePaymentSuccess(paymentId: number, bookingId: number)
booking,
paid: true,
platformClientParams: platformOAuthClient ? getPlatformParams(platformOAuthClient) : undefined,
traceContext: updatedTraceContext,
});
} else {
await handleBookingRequested({
+5 -1
View File
@@ -3,6 +3,7 @@ import getRawBody from "raw-body";
import { z } from "zod";
import { handlePaymentSuccess } from "@calcom/app-store/_utils/payments/handlePaymentSuccess";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import { albyCredentialKeysSchema } from "@calcom/app-store/alby/lib";
import parseInvoice from "@calcom/app-store/alby/lib/parseInvoice";
import { IS_PRODUCTION } from "@calcom/lib/constants";
@@ -88,7 +89,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
throw new HttpCode({ statusCode: 400, message: "invoice amount does not match payment amount" });
}
return await handlePaymentSuccess(payment.id, payment.bookingId);
const traceContext = distributedTracing.createTrace("alby_webhook", {
meta: { paymentId: payment.id, bookingId: payment.bookingId },
});
return await handlePaymentSuccess(payment.id, payment.bookingId, traceContext);
} catch (_err) {
const err = getServerErrorFromUnknown(_err);
console.error(`Webhook Error: ${err.message}`);
@@ -4,6 +4,7 @@ import getRawBody from "raw-body";
import { z } from "zod";
import { handlePaymentSuccess } from "@calcom/app-store/_utils/payments/handlePaymentSuccess";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import { IS_PRODUCTION } from "@calcom/lib/constants";
import { HttpError as HttpCode } from "@calcom/lib/http-error";
import { getServerErrorFromUnknown } from "@calcom/lib/server/getServerErrorFromUnknown";
@@ -87,7 +88,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
);
if (!isValid) throw new HttpCode({ statusCode: 400, message: "signature mismatch" });
await handlePaymentSuccess(payment.id, payment.bookingId);
const traceContext = distributedTracing.createTrace("btcpayserver_webhook", {
meta: { paymentId: payment.id, bookingId: payment.bookingId },
});
await handlePaymentSuccess(payment.id, payment.bookingId, traceContext);
return res.status(200).json({ success: true });
} catch (_err) {
const err = getServerErrorFromUnknown(_err);
+5 -1
View File
@@ -3,6 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import type z from "zod";
import { handlePaymentSuccess } from "@calcom/app-store/_utils/payments/handlePaymentSuccess";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import { IS_PRODUCTION } from "@calcom/lib/constants";
import { HttpError as HttpCode } from "@calcom/lib/http-error";
import { getServerErrorFromUnknown } from "@calcom/lib/server/getServerErrorFromUnknown";
@@ -109,7 +110,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (excluded.status !== "completed") {
throw new HttpCode({ statusCode: 204, message: `Payment is ${excluded.status}` });
}
return await handlePaymentSuccess(payment.id, payment.bookingId);
const traceContext = distributedTracing.createTrace("hitpay_webhook", {
meta: { paymentId: payment.id, bookingId: payment.bookingId },
});
return await handlePaymentSuccess(payment.id, payment.bookingId, traceContext);
} catch (_err) {
const err = getServerErrorFromUnknown(_err);
console.error(`Webhook Error: ${err.message}`);
+5 -1
View File
@@ -3,6 +3,7 @@ import getRawBody from "raw-body";
import { z } from "zod";
import { handlePaymentSuccess } from "@calcom/app-store/_utils/payments/handlePaymentSuccess";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import { paypalCredentialKeysSchema } from "@calcom/app-store/paypal/lib";
import Paypal from "@calcom/app-store/paypal/lib/Paypal";
import { IS_PRODUCTION } from "@calcom/lib/constants";
@@ -62,7 +63,10 @@ export async function handlePaypalPaymentSuccess(
},
});
return await handlePaymentSuccess(payment.id, payment.bookingId);
const traceContext = distributedTracing.createTrace("paypal_webhook", {
meta: { paymentId: payment.id, bookingId: payment.bookingId },
});
return await handlePaymentSuccess(payment.id, payment.bookingId, traceContext);
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -20,6 +20,8 @@ import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import type { TraceContext } from "@calcom/lib/tracing";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import type { PrismaClient } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import type { SchedulingType } from "@calcom/prisma/enums";
@@ -74,6 +76,7 @@ export async function handleConfirmation(args: {
paid?: boolean;
emailsEnabled?: boolean;
platformClientParams?: PlatformClientParams;
traceContext: TraceContext;
}) {
const {
user,
@@ -85,6 +88,7 @@ export async function handleConfirmation(args: {
paid,
emailsEnabled = true,
platformClientParams,
traceContext,
} = args;
const eventType = booking.eventType;
const eventTypeMetadata = EventTypeMetaDataSchema.parse(eventType?.metadata || {});
@@ -96,13 +100,17 @@ export async function handleConfirmation(args: {
const metadata: AdditionalInformation = {};
const workflows = await getAllWorkflowsFromEventType(eventType, booking.userId);
const spanContext = distributedTracing.createSpan(traceContext, "handle_confirmation");
const tracingLogger = distributedTracing.getTracingLogger(spanContext);
if (results.length > 0 && results.every((res) => !res.success)) {
const error = {
errorCode: "BookingCreatingMeetingFailed",
message: "Booking failed",
};
log.error(`Booking ${user.username} failed`, safeStringify({ error, results }));
tracingLogger.error(`Booking ${user.username} failed`, safeStringify({ error, results }));
} else {
if (results.length) {
// TODO: Handle created event metadata more elegantly
@@ -139,7 +147,7 @@ export async function handleConfirmation(args: {
);
}
} catch (error) {
log.error(error);
tracingLogger.error(error);
}
}
let updatedBookings: {
@@ -358,6 +366,7 @@ export async function handleConfirmation(args: {
hideBranding: !!updatedBookings[index].eventType?.owner?.hideBranding,
seatReferenceUid: evt.attendeeSeatId,
isPlatformNoEmail: !emailsEnabled && Boolean(platformClientParams?.platformClientId),
traceContext: spanContext,
});
}
@@ -487,7 +496,7 @@ export async function handleConfirmation(args: {
sub,
payload
).catch((e) => {
log.error(
tracingLogger.error(
`Error executing webhook for event: ${WebhookTriggerEvents.BOOKING_CREATED}, URL: ${sub.subscriberUrl}, bookingId: ${evt.bookingId}, bookingUid: ${evt.uid}, platformClientId: ${platformClientParams?.platformClientId}`,
safeStringify(e)
);
@@ -547,7 +556,7 @@ export async function handleConfirmation(args: {
sub,
payload
).catch((e) => {
log.error(
tracingLogger.error(
`Error executing webhook for event: ${WebhookTriggerEvents.BOOKING_PAID}, URL: ${sub.subscriberUrl}, bookingId: ${evt.bookingId}, bookingUid: ${evt.uid}`,
safeStringify(e)
);
@@ -586,7 +595,10 @@ export async function handleConfirmation(args: {
creditCheckFn: creditService.hasAvailableCredits.bind(creditService),
});
} catch (error) {
log.error("Error while scheduling workflow reminders for booking paid", safeStringify(error));
tracingLogger.error(
"Error while scheduling workflow reminders for booking paid",
safeStringify(error)
);
}
}
} catch (error) {
@@ -34,6 +34,7 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => {
rescheduledBy,
rescheduleReason,
isDryRun = false,
traceContext,
} = newSeatedBookingObject;
// TODO: We could allow doing more things to support good dry run for seats
if (isDryRun) return;
@@ -160,7 +161,7 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => {
rescheduledBy,
};
await handleWebhookTrigger({ subscriberOptions, eventTrigger, webhookData, isDryRun });
await handleWebhookTrigger({ subscriberOptions, eventTrigger, webhookData, isDryRun, traceContext });
}
return resultBooking;
+2
View File
@@ -1,6 +1,7 @@
import type z from "zod";
import type { Workflow } from "@calcom/features/ee/workflows/lib/types";
import type { TraceContext } from "@calcom/lib/tracing";
import type { Prisma } from "@calcom/prisma/client";
import type { AppsStatus, CalendarEvent } from "@calcom/types/Calendar";
@@ -58,6 +59,7 @@ export type NewSeatedBookingObject = {
rescheduledBy?: string;
workflows: Workflow[];
isDryRun?: boolean;
traceContext: TraceContext;
};
export type RescheduleSeatedBookingObject = NewSeatedBookingObject & { rescheduleUid: string };
@@ -2,25 +2,33 @@ import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
import { isEventPayload, type WebhookPayloadType } from "@calcom/features/webhooks/lib/sendPayload";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import { withReporting } from "@calcom/lib/sentryWrapper";
import type { TraceContext } from "@calcom/lib/tracing";
import { distributedTracing } from "@calcom/lib/tracing/factory";
async function _handleWebhookTrigger(args: {
subscriberOptions: GetSubscriberOptions;
eventTrigger: string;
webhookData: WebhookPayloadType;
isDryRun?: boolean;
traceContext: TraceContext;
}) {
const spanContext = distributedTracing.createSpan(args.traceContext, "webhook_trigger", {
eventTrigger: args.eventTrigger || "unknown",
});
const tracingLogger = distributedTracing.getTracingLogger(spanContext);
try {
if (args.isDryRun) return;
const subscribers = await getWebhooks(args.subscriberOptions);
const promises = subscribers.map((sub) =>
sendPayload(sub.secret, args.eventTrigger, new Date().toISOString(), sub, args.webhookData).catch(
(e) => {
if (isEventPayload(args.webhookData)) {
logger.error(
tracingLogger.error(
`Error executing webhook for event: ${args.eventTrigger}, URL: ${sub.subscriberUrl}, booking id: ${args.webhookData.bookingId}, booking uid: ${args.webhookData.uid}`,
safeStringify(e)
);
@@ -30,7 +38,7 @@ async function _handleWebhookTrigger(args: {
);
await Promise.all(promises);
} catch (error) {
logger.error("Error while sending webhook", error);
tracingLogger.error("Error while sending webhook", error);
}
}
@@ -1720,6 +1720,7 @@ async function handler(
workflows,
rescheduledBy: reqBody.rescheduledBy,
isDryRun,
traceContext,
});
if (newBooking) {
@@ -2480,6 +2481,7 @@ async function handler(
paymentId: payment?.id,
},
isDryRun,
traceContext,
});
try {
@@ -2617,6 +2619,7 @@ async function handler(
eventTrigger,
webhookData,
isDryRun,
traceContext,
});
} else {
// if eventType requires confirmation we will trigger the BOOKING REQUESTED Webhook
@@ -2628,6 +2631,7 @@ async function handler(
eventTrigger,
webhookData,
isDryRun,
traceContext,
});
}
@@ -2671,6 +2675,7 @@ async function handler(
seatReferenceUid: evt.attendeeSeatId,
isPlatformNoEmail: noEmail && Boolean(platformClientId),
isDryRun,
traceContext,
});
}
+19 -5
View File
@@ -18,6 +18,8 @@ import { HttpError as HttpCode } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import { getServerErrorFromUnknown } from "@calcom/lib/server/getServerErrorFromUnknown";
import type { TraceContext } from "@calcom/lib/tracing";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import { prisma } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { BookingStatus } from "@calcom/prisma/enums";
@@ -30,7 +32,7 @@ export const config = {
},
};
export async function handleStripePaymentSuccess(event: Stripe.Event) {
export async function handleStripePaymentSuccess(event: Stripe.Event, traceContext: TraceContext) {
const paymentIntent = event.data.object as Stripe.PaymentIntent;
const payment = await prisma.payment.findFirst({
where: {
@@ -48,10 +50,10 @@ export async function handleStripePaymentSuccess(event: Stripe.Event) {
}
if (!payment?.bookingId) throw new HttpCode({ statusCode: 204, message: "Payment not found" });
await handlePaymentSuccess(payment.id, payment.bookingId);
await handlePaymentSuccess(payment.id, payment.bookingId, traceContext);
}
const handleSetupSuccess = async (event: Stripe.Event) => {
const handleSetupSuccess = async (event: Stripe.Event, traceContext: TraceContext) => {
const setupIntent = event.data.object as Stripe.SetupIntent;
const payment = await prisma.payment.findFirst({
where: {
@@ -63,6 +65,10 @@ const handleSetupSuccess = async (event: Stripe.Event) => {
const { booking, user, evt, eventType } = await getBooking(payment.bookingId);
const updatedTraceContext = distributedTracing.updateTrace(traceContext, {
bookingId: booking.id,
});
const bookingData: Prisma.BookingUpdateInput = {
paid: true,
};
@@ -126,6 +132,7 @@ const handleSetupSuccess = async (event: Stripe.Event) => {
booking,
paid: true,
platformClientParams: platformOAuthClient ? getPlatformParams(platformOAuthClient) : undefined,
traceContext: updatedTraceContext,
});
} else if (areEmailsEnabled) {
await sendOrganizerRequestEmail({ ...evt }, eventType.metadata);
@@ -133,7 +140,7 @@ const handleSetupSuccess = async (event: Stripe.Event) => {
}
};
type WebhookHandler = (event: Stripe.Event) => Promise<void>;
type WebhookHandler = (event: Stripe.Event, traceContext: TraceContext) => Promise<void>;
const webhookHandlers: Record<string, WebhookHandler | undefined> = {
"payment_intent.succeeded": handleStripePaymentSuccess,
@@ -163,6 +170,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const event = stripe.webhooks.constructEvent(payload, sig, process.env.STRIPE_WEBHOOK_SECRET);
const traceContext = distributedTracing.createTrace(`stripe_webhook`, {
meta: {
eventType: event.type,
eventId: event.id,
},
});
// bypassing this validation for e2e tests
// in order to successfully confirm the payment
if (!event.account && !process.env.NEXT_PUBLIC_IS_E2E) {
@@ -171,7 +185,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const handler = webhookHandlers[event.type];
if (handler) {
await handler(event);
await handler(event, traceContext);
} else {
/** Not really an error, just letting Stripe know that the webhook was received but unhandled */
throw new HttpCode({
@@ -2,14 +2,13 @@ import type { getEventTypeResponse } from "@calcom/features/bookings/lib/handleN
import { scheduleEmailReminder } from "@calcom/features/ee/workflows/lib/reminders/emailReminderManager";
import type { Workflow } from "@calcom/features/ee/workflows/lib/types";
import type { getDefaultEvent } from "@calcom/features/eventtypes/lib/defaultEvents";
import logger from "@calcom/lib/logger";
import { withReporting } from "@calcom/lib/sentryWrapper";
import type { TraceContext } from "@calcom/lib/tracing";
import { distributedTracing } from "@calcom/lib/tracing/factory";
import { WorkflowTriggerEvents, TimeUnit, WorkflowActions, WorkflowTemplates } from "@calcom/prisma/enums";
import type { ExtendedCalendarEvent } from "./reminderScheduler";
const log = logger.getSubLogger({ prefix: ["[scheduleMandatoryReminder]"] });
export type NewBookingEventType = Awaited<ReturnType<typeof getDefaultEvent>> | getEventTypeResponse;
async function _scheduleMandatoryReminder({
@@ -20,6 +19,7 @@ async function _scheduleMandatoryReminder({
seatReferenceUid,
isPlatformNoEmail = false,
isDryRun = false,
traceContext,
}: {
evt: ExtendedCalendarEvent;
workflows: Workflow[];
@@ -28,7 +28,26 @@ async function _scheduleMandatoryReminder({
seatReferenceUid: string | undefined;
isPlatformNoEmail?: boolean;
isDryRun?: boolean;
traceContext: TraceContext;
}) {
const reminderMeta = {
eventTitle: evt.title,
attendeeCount: evt.attendees.length,
organizerId: evt.organizer.id,
requiresConfirmation,
hideBranding,
seatReferenceUid,
isPlatformNoEmail,
};
const spanContext = distributedTracing.createSpan(
traceContext,
"schedule_mandatory_reminder",
reminderMeta
);
const tracingLogger = distributedTracing.getTracingLogger(spanContext);
if (isDryRun) return;
if (isPlatformNoEmail) return;
try {
@@ -67,11 +86,11 @@ async function _scheduleMandatoryReminder({
verifiedAt: new Date(),
});
} catch (error) {
log.error("Error while scheduling mandatory reminders", JSON.stringify({ error }));
tracingLogger.error("Error while scheduling mandatory reminders", JSON.stringify({ error }));
}
}
} catch (error) {
log.error("Error while scheduling mandatory reminders", JSON.stringify({ error }));
tracingLogger.error("Error while scheduling mandatory reminders", JSON.stringify({ error }));
}
}
@@ -20,6 +20,7 @@ import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
import { getTranslation } from "@calcom/lib/server/i18n";
import { PrismaOrgMembershipRepository } from "@calcom/lib/server/repository/PrismaOrgMembershipRepository";
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
import type { TraceContext } from "@calcom/lib/tracing";
import { prisma } from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import {
@@ -41,6 +42,7 @@ import type { TConfirmInputSchema } from "./confirm.schema";
type ConfirmOptions = {
ctx: {
user: Pick<NonNullable<TrpcSessionUser>, "id" | "uuid" | "email" | "username" | "role" | "destinationCalendar">;
traceContext: TraceContext;
};
input: TConfirmInputSchema;
};
@@ -277,6 +279,15 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
});
}
}
const traceContext = {
...ctx.traceContext,
bookingUid: booking.uid || "unknown",
confirmed: String(confirmed),
eventTypeId: booking.eventType?.id?.toString() || "null",
userId: user.id.toString(),
teamId: booking.eventType?.teamId?.toString() || "null",
};
if (recurringEventId && recurringEvent) {
const groupedRecurringBookings = await prisma.booking.groupBy({
where: {
@@ -307,6 +318,7 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
(booking.eventType?.locations as LocationObject[]) || []
);
evt.conferenceCredentialId = conferenceCredentialId.conferenceCredentialId;
await handleConfirmation({
user: { ...user, credentials: allCredentials },
evt,
@@ -316,6 +328,7 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
booking,
emailsEnabled,
platformClientParams,
traceContext,
});
} else {
evt.rejectionReason = rejectionReason;
@@ -392,7 +405,7 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
status: BookingStatus.REJECTED,
smsReminderNumber: booking.smsReminderNumber || undefined,
};
await handleWebhookTrigger({ subscriberOptions, eventTrigger, webhookData });
await handleWebhookTrigger({ subscriberOptions, eventTrigger, webhookData, traceContext });
const workflows = await getAllWorkflowsFromEventType(booking.eventType, user.id);
try {