← Назад к вопросам
Как используешь мета-данные?
2.0 Middle🔥 81 комментариев
#Архитектура и паттерны#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Использование метаданных в backend приложении
Метаданные — это критически важная информация, которая помогает отслеживать, отлаживать и управлять потоком данных в системе. Расскажу о различных применениях.
1. Трейсинг и корреляция запросов
Correlation ID — главный инструмент:
const express = require('express');
const { v4: uuid } = require('uuid');
const correlationMiddleware = (req, res, next) => {
const correlationId = req.headers['x-correlation-id'] || uuid();
req.correlationId = correlationId;
res.setHeader('X-Correlation-ID', correlationId);
// Добавляем в контекст для логов
req.context = {
correlationId,
timestamp: new Date().toISOString(),
userId: req.user?.id,
requestPath: req.path,
requestMethod: req.method
};
next();
};
app.use(correlationMiddleware);
Использование в логах:
const logger = (req, message, data) => {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
correlationId: req.correlationId,
service: 'order-service',
level: 'info',
message,
data,
userId: req.context.userId
}));
};
app.post('/orders', (req, res) => {
logger(req, 'Creating order', { items: req.body.items.length });
// ...
});
2. Метаданные сообщений (Message Queue)
В RabbitMQ/Kafka:
const publishEvent = async (channel, event) => {
const metadata = {
id: uuid(),
timestamp: new Date().toISOString(),
source: 'order-service',
version: '1.0',
correlationId: getContextId(),
userId: getCurrentUserId(),
traceId: getTraceId(),
retryCount: 0,
expiredAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
};
const message = {
...metadata,
payload: event
};
channel.publish(
'events',
'order.created',
Buffer.from(JSON.stringify(message)),
{
persistent: true,
contentType: 'application/json',
headers: {
'x-correlation-id': metadata.correlationId,
'x-retry-count': metadata.retryCount
},
timestamp: Date.now()
}
);
};
// На стороне потребителя
const consumeEvent = async (msg) => {
const content = JSON.parse(msg.content.toString());
const { id, correlationId, retryCount } = content;
try {
await processEvent(content);
channel.ack(msg);
} catch (error) {
if (retryCount < 3) {
// Переиздание с увеличенным retryCount
content.retryCount = retryCount + 1;
await publishEvent(channel, content);
} else {
// Отправка в dead-letter queue
channel.nack(msg, false, false);
}
}
};
3. Метаданные в базе данных
-- Таблица с метаданными
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
total_amount DECIMAL,
status VARCHAR,
-- Метаданные
correlation_id UUID,
trace_id UUID,
source_service VARCHAR,
-- Аудит
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
created_by_service VARCHAR,
updated_by_service VARCHAR,
-- Версионирование
version INT DEFAULT 1,
-- Мягкое удаление
deleted_at TIMESTAMPTZ,
-- Дополнительные метаданные
metadata JSONB
);
CREATE INDEX idx_orders_correlation_id ON orders(correlation_id);
CREATE INDEX idx_orders_trace_id ON orders(trace_id);
CREATE INDEX idx_orders_user_id ON orders(user_id) WHERE deleted_at IS NULL;
Использование в ORM (TypeORM/Prisma):
const createOrder = async (orderData, context) => {
const order = await Order.create({
userId: orderData.userId,
amount: orderData.amount,
// Метаданные
correlationId: context.correlationId,
traceId: context.traceId,
sourceService: context.service,
createdByService: context.service,
// JSON метаданные
metadata: {
ipAddress: context.ipAddress,
userAgent: context.userAgent,
country: context.country,
source: 'web' // или 'mobile', 'api'
}
});
return order;
};
4. Контекст запроса (Request Context)
// middleware для создания контекста
const createRequestContext = (req, res, next) => {
const context = {
// Идентификация
correlationId: req.headers['x-correlation-id'] || uuid(),
traceId: req.headers['x-trace-id'] || uuid(),
requestId: uuid(),
// Пользователь
userId: req.user?.id,
userRole: req.user?.role,
// Сервис
service: process.env.SERVICE_NAME,
version: process.env.APP_VERSION,
// Окружение
environment: process.env.NODE_ENV,
timestamp: new Date().toISOString(),
// Техническая информация
ipAddress: req.ip,
userAgent: req.headers['user-agent'],
method: req.method,
path: req.path,
// Performance
startTime: Date.now()
};
req.context = context;
next();
};
// Использование в handlers
const updateOrder = async (req, res) => {
const { correlationId, userId, startTime } = req.context;
try {
const order = await Order.findById(req.params.id);
// Проверка прав доступа
if (order.userId !== userId) {
logger.warn('Unauthorized access attempt', {
correlationId,
userId,
orderId: order.id,
duration: Date.now() - startTime
});
return res.status(403).json({ error: 'Forbidden' });
}
await order.update(req.body);
res.json(order);
} catch (error) {
logger.error('Order update failed', {
correlationId,
error: error.message,
duration: Date.now() - startTime
});
}
};
5. Кэширование с метаданными
const getCachedUser = async (userId, context) => {
const cacheKey = `user:${userId}`;
const cached = await redis.get(cacheKey);
if (cached) {
const data = JSON.parse(cached);
logger.debug('Cache hit', {
correlationId: context.correlationId,
userId,
cachedAt: data.metadata.cachedAt,
age: Date.now() - data.metadata.cachedAt
});
return data.user;
}
const user = await User.findById(userId);
// Кешируем с метаданными
await redis.setex(
cacheKey,
3600,
JSON.stringify({
user,
metadata: {
cachedAt: Date.now(),
cachedBy: context.service,
correlationId: context.correlationId,
version: 1
}
})
);
return user;
};
6. Аудит операций
const auditLog = async (operation, entity, changes, context) => {
await AuditLog.create({
id: uuid(),
correlationId: context.correlationId,
userId: context.userId,
service: context.service,
operation, // 'CREATE', 'UPDATE', 'DELETE'
entityType: entity.constructor.name,
entityId: entity.id,
changes, // diff между старым и новым значением
timestamp: new Date(),
metadata: {
ipAddress: context.ipAddress,
userAgent: context.userAgent,
reason: context.auditReason
}
});
};
// Использование
const updateOrder = async (req, res) => {
const oldOrder = { ...order };
await order.update(req.body);
await auditLog('UPDATE', order, {
before: oldOrder,
after: order
}, req.context);
};
7. Мониторинг и метрики
const prometheus = require('prom-client');
const httpRequestDuration = new prometheus.Histogram({
name: 'http_request_duration_ms',
help: 'Duration of HTTP requests in ms',
labelNames: ['method', 'route', 'status_code', 'service', 'correlation_id'],
buckets: [0.1, 5, 15, 50, 100]
});
const metricsMiddleware = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
httpRequestDuration
.labels(
req.method,
req.route?.path || 'unknown',
res.statusCode,
process.env.SERVICE_NAME,
req.context.correlationId
)
.observe(duration);
});
next();
};
Ключевые метаданные для отслеживания
- Correlation ID — связывает все операции одного запроса
- Trace ID — для distributed tracing (Jaeger, Datadog)
- Timestamp — когда произошла операция
- User ID — кто выполнил операцию
- Service Name — какой сервис
- Request ID — уникальный ID запроса
- Version — версия данных/API
- IP Address — источник запроса
- Duration — время выполнения
- Retry Count — количество попыток переиздания
Метаданные — это основа для отладки, аудита и мониторинга production систем. Без них сложно отследить проблемы в микросервисной архитектуре.