Сценарий 15: Помощь в отладке

Сценарий 15: Помощь в отладке

Разработчик сталкивается с проблемами отладки, связанными с навигацией по коду и его анализом.

Содержание

Быстрый старт

# Выберите сценарий отладки
/select 15

Трассировка цепочки вызовов

Трассировка выполнения функции

> Трассировка цепочки вызовов до heap_insert

╭─────────────── Цепочка вызовов ─────────────────────────────────────────────╮
                                                                            
  Цель: heap_insert()                                                       
  Расположение: src/backend/access/heap/heapam.c:2156                        
                                                                            
  Цепочки вызовов (топ-3 по частоте):                                        
                                                                            
  Путь 1 (оператор INSERT):                                                 
    exec_simple_query()                                                     
      └── PortalRun()                                                       
            └── ExecutorRun()                                               
                  └── ExecModifyTable()                                     
                        └── ExecInsert()                                    
                              └── table_tuple_insert()                      
                                    └── heap_insert()                      
                                                                            
  Путь 2 (команда COPY):                                                    
    DoCopy()                                                                
      └── CopyFrom()                                                        
            └── CopyMultiInsertBufferFlush()                                
                  └── heap_insert()                                        
                                                                            
  Путь 3 (выполнение триггера):                                             
    ExecCallTriggerFunc()                                                   
      └── heap_insert()                                                    
                                                                            
╰────────────────────────────────────────────────────────────────────────────╯

Обратная трассировка (вызываемые функции)

> Какие функции вызывает heap_insert?

╭─────────────── Вызываемые функции ────────────────────────────────────────╮
│                                                                            │
│  heap_insert() вызывает:                                                   │
│                                                                            │
│  Память/Буфер:                                                             │
│    RelationGetBufferForTuple()  - Получить целевой буфер                   │
│    BufferGetPage()              - Получить страницу из буфера              │
│    PageAddItem()                - Добавить элемент на страницу             │
│    MarkBufferDirty()            - Пометить для записи                       │
│                                                                            │
│  Транзакции:                                                               │
│    GetCurrentTransactionId()    - Получить XID                             │
│    TransactionIdStore()         - Сохранить XID в кортеже                  │
│                                                                            │
│  Блокировки:                                                               │
│    LockBuffer()                 - Заблокировать буфер                       │
│    UnlockReleaseBuffer()        - Разблокировать и освободить               │
│                                                                            │
│  Ведение логов:                                                            │
│    XLogInsert()                 - Запись в журнал WAL                      │
│    log_heap_insert()            - Специфичная для кучи запись в WAL        │
│                                                                            │
│  Всего прямых вызовов: 23                                                  │
│                                                                            │
╰────────────────────────────────────────────────────────────────────────────╯

Анализ путей ошибок

Поиск обработчиков ошибок

> Какие обработчики ошибок существуют для операций с кучей?

╭─────────────── Обработчики ошибок ──────────────────────────────╮
│                                                                │
│  Обработка ошибок в модуле кучи:                                │
│                                                                │
│  Места вызова ereport(ERROR):                                  │
│                                                                │
│  heap_insert():                                                │
│    Строка 2178: "tuple too big" (слишком большой кортеж)       │
│    Строка 2189: "no free space on page" (нет свободного места на странице) │
│                                                                │
│  heap_update():                                                │
│    Строка 3456: "concurrent update detected" (обнаружено параллельное обновление) │
│    Строка 3478: "tuple updated by different transaction" (кортеж обновлен другой транзакцией) │
│                                                                │
│  heap_delete():                                                │
│    Строка 2890: "tuple already deleted" (кортеж уже удалён)    │
│                                                                │
│  Блоки PG_TRY/PG_CATCH:                                        │
│    heap_vacuum_rel(): 2 обработчика исключений                 │
│    heap_prepare_insert(): 1 обработчик исключений              │
│                                                                │
│  Всего путей ошибок: 15                                        │
│                                                                │
╰────────────────────────────────────────────────────────────────╯

Поток исключений

> Проследить, что происходит при сбое heap_insert

╭─────────────── Поток исключений ──────────────────────────────╮
│                                                              │
│  Исключение: "tuple too big" в функции heap_insert()          │
│                                                              │
│  Путь исключения:                                           │
│    heap_insert() вызывает ERROR                              │
│      ↓                                                       │
│    PG_CATCH в ExecModifyTable()                              │
│      ↓ (нет специфического обработчика, повторное возбуждение) │
│    PG_CATCH в ExecutorRun()                                  │
│      ↓ (очистка, повторное возбуждение)                       │
│    PG_CATCH в PortalRun()                                    │
│      ↓ (прерывание транзакции)                               │
│    PG_CATCH в PostgresMain()                                 │
│      → Отправка ошибки клиенту                                │
│      → Откат транзакции                                       │
│      → Продолжение основного цикла                            │
│                                                              │
│  Освобождённые ресурсы:                                       │
│    - Освобождение буферов                                     │
│    - Освобождение блокировок                                  │
│    - Сброс контекстов памяти                                  │
│                                                              │
╰──────────────────────────────────────────────────────────────╯

Анализ состояния

Отслеживание переменных

> Отслеживание состояния переменной 'tuple' в функции heap_insert

╭─────────────── Состояние переменной ──────────────────────────────╮
│                                                                   │
│  Переменная: tuple (HeapTuple)                                    │
│  Функция: heap_insert()                                           │
│                                                                   │
│  Переходы состояний:                                              │
│                                                                   │
│  Строка 2160: [ВХОД] Получена от вызывающего кода                 │
│             Состояние: Инициализирована, OID отсутствует          │
│                                                                   │
│  Строка 2165: [ИЗМЕНЕНО] HeapTupleSetOid()                         │
│             Состояние: Присвоен OID                               │
│                                                                   │
│  Строка 2170: [ИЗМЕНЕНО] HeapTupleHeaderSetXmin()                  │
│             Состояние: Xmin установлен в текущий XID              │
│                                                                   │
│  Строка 2178: [ПРОВЕРЕНО] Проверка размера кортежа                │
│             Состояние: Проверено или ОШИБКА                       │
│                                                                   │
│  Строка 2190: [ИЗМЕНЕНО] PageAddItem()                             │
│             Состояние: Сохранено на странице буфера               │
│                                                                   │
│  Строка 2210: [ВЫХОД] Возврат вызывающему коду                    │
│             Состояние: Полностью вставлено, записано в WAL        │
│                                                                   │
╰───────────────────────────────────────────────────────────────────╯

Отладка рабочих процессов

Быстрая контрольная панель отладки

> Создать контрольный список отладки для проблем с heap_insert

╭────────────── Контрольный список отладки ─────────────────────────────╮
│                                                                     │
│  Отладка проблем с heap_insert():                                    │
│                                                                     │
│  1. Проверка корректности входных данных                             │
│     □ Корректна ли и открыта ли реляция?                             │
│     □ Правильно ли сформирован кортеж?                               │
│     □ Укладывается ли размер кортежа в допустимые пределы?           │
│                                                                     │
│  2. Проверка состояния транзакции                                   │
│     □ Имеется ли активная транзакция?                                │
│     □ Действителен ли идентификатор транзакции?                      │
│     □ Проверить возможность переполнения идентификаторов транзакций  │
│                                                                     │
│  3. Проверка состояния буфера                                       │
│     □ Правильно ли установлены блокировки буфера?                    │
│     □ Имеется ли свободное место на странице?                        │
│     □ Установлен ли флаг "грязного" буфера?                          │
│                                                                     │
│  4. Проверка состояния WAL                                          │
│     □ Включено ли ведение журнала WAL?                               │
│     □ Успешно ли выполнена запись в WAL?                             │
│     □ Проверить наличие свободного места в буфере WAL               │
│                                                                     │
│  5. Типичные ошибки                                                 │
│     □ "кортеж слишком большой" → Проверить размер кортежа           │
│     □ "нет свободного места" → Проверить автосборку мусора           │
│     □ "взаимоблокировка" → Проверить порядок установки блокировок    │
│                                                                     │
╰─────────────────────────────────────────────────────────────────────╯

Примеры вопросов

  • «Проследить путь вызова до [функции]»
  • «Какие функции вызывает [функция]?»
  • «Найти обработчики ошибок в [модуле]»
  • «Что происходит, если [функция] завершается с ошибкой?»
  • «Отследить переменную [имя] внутри [функции]»
  • «Создать контрольный список отладки для [проблемы]»