← Назад к вопросам

Как используешь мета-данные?

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();
};

Ключевые метаданные для отслеживания

  1. Correlation ID — связывает все операции одного запроса
  2. Trace ID — для distributed tracing (Jaeger, Datadog)
  3. Timestamp — когда произошла операция
  4. User ID — кто выполнил операцию
  5. Service Name — какой сервис
  6. Request ID — уникальный ID запроса
  7. Version — версия данных/API
  8. IP Address — источник запроса
  9. Duration — время выполнения
  10. Retry Count — количество попыток переиздания

Метаданные — это основа для отладки, аудита и мониторинга production систем. Без них сложно отследить проблемы в микросервисной архитектуре.

Как используешь мета-данные? | PrepBro