Приложение на Go (архивная версия v2)

На этой странице подробно разбирается код тестового приложения, доступного в составе v2 Go SDK YDB.

Инициализация соединения с базой данных

Для взаимодействия с YDB создается экземпляр драйвера, клиента и сессии:

  • Драйвер YDB отвечает за взаимодействие приложения и YDB на транспортном уровне. Драйвер должен существовать на всем протяжении жизненного цикла работы с YDB и должен быть инициализирован перед созданием клиента и сессии.
  • Клиент YDB работает поверх драйвера YDB и отвечает за работу с сущностями и транзакциями.
  • Сессия YDB содержит информацию о выполняемых транзакциях и подготовленных запросах и содержится в контексте клиента YDB.

Для работы с YDB в go следует импортировать пакет драйвера ydb-go-sdk:

import (
  // общие импорты
  "context"
  "path"

  // импорты пакетов ydb-go-sdk
  "github.com/yandex-cloud/ydb-go-sdk/v2"
  "github.com/yandex-cloud/ydb-go-sdk/v2/table" // для работы с table-сервисом
)

Фрагмент кода приложения для инициализации драйвера:

func (cmd *Command) Run(ctx context.Context, params cli.Parameters) error {
  dialer := &ydb.Dialer{
    DriverConfig: cmd.config(params),
    TLSConfig:    cmd.tls(),
    Timeout:      time.Second,
  }
  driver, err := dialer.Dial(ctx, params.Endpoint)
  if err != nil {
    return fmt.Errorf("dial error: %v", err)
  }
  defer driver.Close()

Фрагмент кода приложения для создания сессии:

tableClient := table.Client{
  Driver: driver,
}
sp := table.SessionPool{
  IdleThreshold: time.Second,
  Builder:       &tableClient,
}
defer sp.Close(ctx)

Создание таблиц

Выполняется создание таблиц, которые используются в дальнейших операциях тестового приложения. В результате исполнения шага в базе данных будут созданы таблицы модели данных справочника сериалов:

  • series - Сериалы
  • seasons - Сезоны
  • episodes - Эпизоды

После создания вызывается метод получения информации об объекте схемы данных, и выводится результат его выполнения.

Для создания таблиц используется метод Session.CreateTable():

func createTables(ctx context.Context, sp *table.SessionPool, prefix string) (err error) {
  err = table.Retry(ctx, sp,
    table.OperationFunc(func(ctx context.Context, s *table.Session) error {
      return s.CreateTable(ctx, path.Join(prefix, "series"),
        table.WithColumn("series_id", ydb.Optional(ydb.TypeUint64)),
        table.WithColumn("title", ydb.Optional(ydb.TypeUTF8)),
        table.WithColumn("series_info", ydb.Optional(ydb.TypeUTF8)),
        table.WithColumn("release_date", ydb.Optional(ydb.TypeUint64)),
        table.WithColumn("comment", ydb.Optional(ydb.TypeUTF8)),
        table.WithPrimaryKeyColumn("series_id"),
      )
    }),
  )

С помощью метода Session.DescribeTable() можно вывести информацию о структуре таблицы и убедиться, что она успешно создалась:

func describeTable(ctx context.Context, sp *table.SessionPool, path string) (err error) {
  err = table.Retry(ctx, sp,
    table.OperationFunc(func(ctx context.Context, s *table.Session) error {
      desc, err := s.DescribeTable(ctx, path)
      if err != nil {
        return err
      }
      log.Printf("\n> describe table: %s", path)
      for _, c := range desc.Columns {
        log.Printf("column, name: %s, %s", c.Type, c.Name)
      }
      return nil
    }),
  )

Получение выборки данных

Выполняется запрос на получение выборки данных с использованием команды SELECT языка запросов YQL. Демонстрируется обработка полученной выборки в приложении.

Для выполнения YQL-запросов используется метод Session.Execute().
SDK позволяет в явном виде контролировать выполнение транзакций и настраивать необходимый режим выполнения транзакций с помощью класса TxControl.

var (
  query = `--!syntax_v1
    DECLARE $seriesID AS Uint64;
    $format = DateTime::Format("%Y-%m-%d");
    SELECT
      series_id,
      title,
      $format(DateTime::FromSeconds(CAST(DateTime::ToSeconds(DateTime::IntervalFromDays(CAST(release_date AS Int16))) AS Uint32))) AS release_date
    FROM
      series
    WHERE
      series_id = $seriesID;`
  res *table.Result
)
readTx := table.TxControl(
  table.BeginTx(
    table.WithOnlineReadOnly(),
  ),
  table.CommitTx(),
)
err = table.Retry(ctx, sp,
  table.OperationFunc(func(ctx context.Context, s *table.Session) (err error) {
    _, res, err = s.Execute(ctx, readTx, query,
      table.NewQueryParameters(
        table.ValueParam("$seriesID", ydb.Uint64Value(1)),
      ),
      table.WithQueryCachePolicy(
        table.WithQueryCachePolicyKeepInCache(),
      ),
      table.WithCollectStatsModeBasic(),
    )
    return
  }),
)
if err != nil {
  return err
}

Обработка результатов выполнения

Результат выполнения запроса:

  var (
    id    *uint64
    title *string
    date  *[]byte
  )
  log.Println("> select_simple_transaction:")
  for res.NextResultSet(ctx, "series_id", "title", "release_date") {
    for res.NextRow() {
      err = res.Scan(&id, &title, &date)
      if err != nil {
        return err
      }
      log.Printf(
        "#  SeriesID: %d , Title: %s, Date: %s\n",
        *id, *title, *date,
      )
    }
  }
  if err = res.Err(); err != nil {
    return err
  }
  return nil
}

Скан запросы

Выполняется скан запрос данных, результатом исполнения которого является стрим. Стрим позволяет считать неограниченное количество строк и объем данных.

var (
  query = `
    SELECT series_id, season_id, COUNT(*) AS episodes_count
    FROM episodes
    GROUP BY series_id, season_id
    ORDER BY series_id, season_id;`
  res *table.Result
)
err = table.Retry(ctx, sp,
  table.OperationFunc(func(ctx context.Context, s *table.Session) (err error) {
    res, err = s.StreamExecuteScanQuery(ctx, query, table.NewQueryParameters())
    return err
  }),
)
if err != nil {
  return err
}

Результат выполнения запроса:

  var (
    seriesID uint64
    seasonID uint64
    count    uint64
  )
  log.Println("> scan_query_select:")
  for res.NextResultSet(ctx, "series_id", "season_id", "episodes_count") {
    for res.NextRow() {
      err = res.ScanWithDefaults(&seriesID, &seasonID, &count)
      if err != nil {
        return err
      }
      log.Printf("#  Season, SeriesId: %d, SeasonId: %d, Count: %d\n", seriesID, seasonID, count)
    }
  }
  if err = res.Err(); err != nil {
    return err
  }
  return nil
}

Обработка ошибок

Подробно об обработке ошибок написано в разделе Обработка ошибок в API.