Как заставить промежуточное ПО транзакций Buffalo фиксироваться?

При попытке использовать промежуточное ПО buffalo-pop/pop/popmw Transaction мне не удалось записать в базу данных. Ошибки не возвращаются, а выходные данные отладки показывают операторы SQL, но обновления и вставки не фиксируются.

Обработчик выглядит так:


func MyHandler(c buffalo.Context) error {
    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.New("no transaction found")
    }

    f := models.File{
        Name: "file.txt",
    }
    if err := tx.Create(&f); err != nil {
        return err
    }

    return nil
}

app.go:

func App() *buffalo.App {
...
        app.GET("/myhandler", MyHandler)
        app.Use(popmw.Transaction(models.DB))
...
}

Если я использую DB, _ := pop.Connect("development") для своего соединения, оно работает правильно. Я также заметил, что значение автоинкремента в таблице меняется каждый раз при попадании в этот обработчик.

В реальном приложении мы не можем вызвать c.Render, чтобы сообщить код ответа, потому что мы используем gqlgen в качестве обработчика http. Это выглядит так:

func GQLHandler(c buffalo.Context) error {
    h := handler.GraphQL(gqlgen.NewExecutableSchema(gqlgen.Config{Resolvers: &gqlgen.Resolver{}}))
    newCtx := context.WithValue(c.Request().Context(), "BuffaloContext", c)
    h.ServeHTTP(c.Response(), c.Request().WithContext(newCtx))

    return nil
}

person Schparky    schedule 21.11.2019    source источник


Ответы (2)


Одной из особенностей ПО промежуточного слоя Pop для Buffalo является помещение действия и ПО промежуточного слоя ниже в стек внутри транзакции БД. Вот условия автоматической фиксации от Pop Middleware:

  • Подтвердить, если при выполнении промежуточного программного обеспечения и действия не было ошибок; и статус ответа — 2xx или 3xx.
  • Откат в противном случае.

Из интеграции Buffalo с Pop.

Итак, убедитесь, что ни в вашем действии, ни в промежуточном программном обеспечении стека не возвращается ошибка; и полученный статус ответа равен 200 или 300.

person Stanislas Michalak    schedule 21.11.2019
comment
Спасибо, тогда return nil не подходит. Я попытался добавить c.Render(200, r.JSON("")), и это сработало. Проблема в том, что в реальном приложении мы используем обработчик не Buffalo, а именно gqlgen. Любые предложения о том, как дать Buffalo код ответа от этого обработчика? - person Schparky; 22.11.2019

Если Buffalo не получает код ответа при вызове c.Render, ПО промежуточного слоя Transaction рассматривает запрос как неуспешный. Поскольку контекст этого вопроса — GraphQL с использованием gqlgen, а c.Render использовать нельзя, я обнаружил, что явное закрытие транзакции работает. Что-то вроде этого:

func GQLHandler(c buffalo.Context) error {
    gqlSuccess := true
    h := handler.GraphQL(gqlgen.NewExecutableSchema(gqlgen.Config{Resolvers: &gqlgen.Resolver{}})),
        handler.ErrorPresenter(
            func(ctx context.Context, e error) *gqlerror.Error {
                gqlSuccess = false
                return graphql.DefaultErrorPresenter(ctx, e)
            }))
    newCtx := context.WithValue(c.Request().Context(), "BuffaloContext", c)
    h.ServeHTTP(c.Response(), c.Request().WithContext(newCtx))

    if !gqlSuccess {
        return nil
    }

    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.New("no transaction found")
    }
    return tx.TX.Commit()
}
person Schparky    schedule 22.11.2019