vapor / mysql-nio

🐬 Non-blocking, event-driven Swift client for MySQL.
MIT License
87 stars 28 forks source link

Error Selecting From MySQL database on Linux #39

Closed jaredanderton closed 3 years ago

jaredanderton commented 4 years ago

Hello, I am new to Vapor and running into some odd issues when I try to select from a MySQL database. It works locally (on OSX), but it does not work when I deploy to a prod environment.

In production, my docker container is running on a host in AWS EC2, connecting to an RDS instance. My other containers (which are not vapor 4.0) on the host can connect. Also, this is not a connection issue, I have command ("moments") which can successfully insert into the database. However, it seems as soon as I select there is a problem reading bytes.

It works locally, and so I cannot replicate locally, but fails every time I try to select using code like this:

LogMoment.query(on: req.db).limit(1).all()

MySQL Version: 5.6.37

The app was created using vapor-beta, because I wanted the app dockerized.

Here is the error:

Fatal error: file /build/.build/checkouts/mysql-nio/Sources/MySQLNIO/MySQLQueryCommand.swift, line 134
0x7fadc3c6088f
0x7fadc3fbd4e5
0x562aecb567d5, MySQLNIO.(MySQLQueryCommand in _2ACF6639C25D14CC4B926B0BFBB183FB).handle(packet: inout MySQLNIO.MySQLPacket, capabilities: MySQLNIO.MySQLProtocol.CapabilityFlags) throws -> MySQLNIO.MySQLCommandState at /build/.build/checkouts/mysql-nio/Sources/MySQLNIO/MySQLQueryCommand.swift:0
0x562aecb56b8f, protocol witness for MySQLNIO.MySQLCommand.handle(packet: inout MySQLNIO.MySQLPacket, capabilities: MySQLNIO.MySQLProtocol.CapabilityFlags) throws -> MySQLNIO.MySQLCommandState in conformance MySQLNIO.(MySQLQueryCommand in _2ACF6639C25D14CC4B926B0BFBB183FB) : MySQLNIO.MySQLCommand in MySQLNIO at /build/<compiler-generated>:0
0x562aecb417fe, MySQLNIO.MySQLConnectionHandler.channelRead(context: NIO.ChannelHandlerContext, data: NIO.NIOAny) -> () at /build/.build/checkouts/mysql-nio/Sources/MySQLNIO/MySQLConnectionHandler.swift:72
0x562aecbb1ee2, NIO.ChannelHandlerContext.(invokeChannelRead in _EEC863903996E9F191EBAFEB0FB0DFDD)(NIO.NIOAny) -> () at /build/.build/checkouts/swift-nio/Sources/NIO/ChannelPipeline.swift:1339
0x562aecbb1f14, NIO.ChannelHandlerContext.(invokeChannelRead in _EEC863903996E9F191EBAFEB0FB0DFDD)(NIO.NIOAny) -> () at /build/.build/checkouts/swift-nio/Sources/NIO/ChannelPipeline.swift:1341
0x562aecbae102, NIO.ChannelHandlerContext.fireChannelRead(NIO.NIOAny) -> () at /build/.build/checkouts/swift-nio/Sources/NIO/ChannelPipeline.swift:1152
0x562aecb5db00, MySQLNIO.MySQLPacketDecoder.decode(context: NIO.ChannelHandlerContext, buffer: inout NIO.ByteBuffer) throws -> NIO.DecodingState at /build/.build/checkouts/mysql-nio/Sources/MySQLNIO/Packet/MySQLPacketDecoder.swift:32
0x562aecb5db78, protocol witness for NIO.ByteToMessageDecoder.decode(context: NIO.ChannelHandlerContext, buffer: inout NIO.ByteBuffer) throws -> NIO.DecodingState in conformance MySQLNIO.MySQLPacketDecoder : NIO.ByteToMessageDecoder in MySQLNIO at /build/<compiler-generated>:0
0x562aecbc96f6, closure vapor/fluent-mysql-driver#1 (inout A, inout NIO.ByteBuffer) throws -> NIO.DecodingState in NIO.ByteToMessageHandler.(decodeLoop in _F2A740607FEBA425AF6F8C49A24C0AD7)(context: NIO.ChannelHandlerContext, decodeMode: NIO.ByteToMessageHandler<A>.(DecodeMode in _F2A740607FEBA425AF6F8C49A24C0AD7)) throws -> NIO.(B2MDBuffer in _F2A740607FEBA425AF6F8C49A24C0AD7).BufferProcessingResult at /build/.build/checkouts/swift-nio/Sources/NIO/Codec.swift:567
0x562aecbc947e, NIO.ByteToMessageHandler.(withNextBuffer in _F2A740607FEBA425AF6F8C49A24C0AD7)(allowEmptyBuffer: Swift.Bool, _: (inout A, inout NIO.ByteBuffer) throws -> NIO.DecodingState) throws -> NIO.(B2MDBuffer in _F2A740607FEBA425AF6F8C49A24C0AD7).BufferProcessingResult at /build/.build/checkouts/swift-nio/Sources/NIO/Codec.swift:529
0x562aecbc947e, NIO.ByteToMessageHandler.(decodeLoop in _F2A740607FEBA425AF6F8C49A24C0AD7)(context: NIO.ChannelHandlerContext, decodeMode: NIO.ByteToMessageHandler<A>.(DecodeMode in _F2A740607FEBA425AF6F8C49A24C0AD7)) throws -> NIO.(B2MDBuffer in _F2A740607FEBA425AF6F8C49A24C0AD7).BufferProcessingResult at /build/.build/checkouts/swift-nio/Sources/NIO/Codec.swift:563
0x562aecbc9cf6, NIO.ByteToMessageHandler.channelRead(context: NIO.ChannelHandlerContext, data: NIO.NIOAny) -> () at /build/.build/checkouts/swift-nio/Sources/NIO/Codec.swift:636
0x562aecbb1ee2, NIO.ChannelHandlerContext.(invokeChannelRead in _EEC863903996E9F191EBAFEB0FB0DFDD)(NIO.NIOAny) -> () at /build/.build/checkouts/swift-nio/Sources/NIO/ChannelPipeline.swift:1339
0x562aecb914b6, NIO.ChannelPipeline.fireChannelRead0(NIO.NIOAny) -> () at .build/checkouts/swift-nio/Sources/NIO/ChannelPipeline.swift:829
0x562aecb914b6, NIO.BaseStreamSocketChannel.readFromSocket() throws -> NIO.BaseSocketChannel<A>.ReadResult at /build/.build/checkouts/swift-nio/Sources/NIO/BaseStreamSocketChannel.swift:107
0x562aecb8bf13
0x562aecc2658a, generic specialization <NIO.Socket> of NIO.BaseSocketChannel.readable() -> () at .build/checkouts/swift-nio/Sources/NIO/BaseSocketChannel.swift:1048
0x562aecc2658a, inlined generic function <NIO.Socket> of protocol witness for NIO.SelectableChannel.readable() -> () in conformance NIO.BaseSocketChannel<A> : NIO.SelectableChannel in NIO at /build/<compiler-generated>:1045
0x562aecc2658a, function signature specialization <Arg[2] = Dead> of generic specialization <NIO.SocketChannel> of NIO.SelectableEventLoop.handleEvent<A where A: NIO.SelectableChannel>(_: NIO.SelectorEventSet, channel: A) -> () at /build/.build/checkouts/swift-nio/Sources/NIO/SelectableEventLoop.swift:323
0x562aecc23f4f, generic specialization <NIO.SocketChannel> of NIO.SelectableEventLoop.handleEvent<A where A: NIO.SelectableChannel>(_: NIO.SelectorEventSet, channel: A) -> () at /build/<compiler-generated>:0
0x562aecc23f4f, closure vapor/fluent-mysql-driver#1 (NIO.SelectorEvent<NIO.NIORegistration>) -> () in closure vapor/fluent-mysql-driver#2 () throws -> () in NIO.SelectableEventLoop.run() throws -> () at /build/.build/checkouts/swift-nio/Sources/NIO/SelectableEventLoop.swift:393
0x562aecc256c3, reabstraction thunk helper from @callee_guaranteed (@guaranteed NIO.SelectorEvent<NIO.NIORegistration>) -> (@error @owned Swift.Error) to @escaping @callee_guaranteed (@in_guaranteed NIO.SelectorEvent<NIO.NIORegistration>) -> (@error @owned Swift.Error) at /build/<compiler-generated>:0
0x562aecc256c3, partial apply forwarder for reabstraction thunk helper from @callee_guaranteed (@guaranteed NIO.SelectorEvent<NIO.NIORegistration>) -> (@error @owned Swift.Error) to @escaping @callee_guaranteed (@in_guaranteed NIO.SelectorEvent<NIO.NIORegistration>) -> (@error @owned Swift.Error) at /build/<compiler-generated>:0
0x562aecc2912f, NIO.Selector.whenReady(strategy: NIO.SelectorStrategy, _: (NIO.SelectorEvent<A>) throws -> ()) throws -> () at /build/.build/checkouts/swift-nio/Sources/NIO/Selector.swift:620
0x562aecc228b7, closure vapor/fluent-mysql-driver#2 () throws -> () in NIO.SelectableEventLoop.run() throws -> () at /build/.build/checkouts/swift-nio/Sources/NIO/SelectableEventLoop.swift:388
0x562aecc228b7, reabstraction thunk helper from @callee_guaranteed () -> (@error @owned Swift.Error) to @escaping @callee_guaranteed () -> (@out (), @error @owned Swift.Error) at /build/<compiler-generated>:0
0x562aecc228b7, generic specialization <()> of NIO.withAutoReleasePool<A>(() throws -> A) throws -> A at /build/.build/checkouts/swift-nio/Sources/NIO/SelectableEventLoop.swift:26
0x562aecc228b7, NIO.SelectableEventLoop.run() throws -> () at /build/.build/checkouts/swift-nio/Sources/NIO/SelectableEventLoop.swift:387
0x562aecbd9865, static NIO.MultiThreadedEventLoopGroup.(runTheLoop in _D5D78C61B22284700B9BD1ACFBC25157)(thread: NIO.NIOThread, canEventLoopBeShutdownIndividually: Swift.Bool, selectorFactory: () throws -> NIO.Selector<NIO.NIORegistration>, initializer: (NIO.NIOThread) -> (), _: (NIO.SelectableEventLoop) -> ()) -> () at /build/.build/checkouts/swift-nio/Sources/NIO/EventLoop.swift:822
0x562aecbd9865, closure vapor/fluent-mysql-driver#1 (NIO.NIOThread) -> () in static NIO.MultiThreadedEventLoopGroup.(setupThreadAndEventLoop in _D5D78C61B22284700B9BD1ACFBC25157)(name: Swift.String, selectorFactory: () throws -> NIO.Selector<NIO.NIORegistration>, initializer: (NIO.NIOThread) -> ()) -> NIO.SelectableEventLoop at /build/.build/checkouts/swift-nio/Sources/NIO/EventLoop.swift:842
0x562aecbde919, partial apply forwarder for closure vapor/fluent-mysql-driver#1 (NIO.NIOThread) -> () in static NIO.MultiThreadedEventLoopGroup.(setupThreadAndEventLoop in _D5D78C61B22284700B9BD1ACFBC25157)(name: Swift.String, selectorFactory: () throws -> NIO.Selector<NIO.NIORegistration>, initializer: (NIO.NIOThread) -> ()) -> NIO.SelectableEventLoop at /build/<compiler-generated>:0
0x562aecc3b9de, reabstraction thunk helper from @escaping @callee_guaranteed (@guaranteed NIO.NIOThread) -> () to @escaping @callee_guaranteed (@in_guaranteed NIO.NIOThread) -> (@out ()) at /build/<compiler-generated>:0
0x562aecbde930, partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed (@guaranteed NIO.NIOThread) -> () to @escaping @callee_guaranteed (@in_guaranteed NIO.NIOThread) -> (@out ()) at /build/<compiler-generated>:0
0x562aecc3cbdd, closure vapor/fluent-mysql-driver#1 (Swift.Optional<Swift.UnsafeMutableRawPointer>) -> Swift.Optional<Swift.UnsafeMutableRawPointer> in static NIO.ThreadOpsPosix.run(handle: inout Swift.Optional<Swift.UInt>, args: NIO.Box<(body: (NIO.NIOThread) -> (), name: Swift.Optional<Swift.String>)>, detachThread: Swift.Bool) -> () at /build/.build/checkouts/swift-nio/Sources/NIO/ThreadPosix.swift:105
0x7fadc3c556da
0x7fadc1d9088e
0xffffffffffffffff

Package.swift:

import PackageDescription

let package = Package(
    name: "tickertracker",
    platforms: [
       .macOS(.v10_15)
    ],
    dependencies: [
        // 💧 A server-side Swift web framework.
        .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0-rc"),
        .package(url: "https://github.com/vapor/fluent.git", from: "4.0.0-rc"),
        .package(url: "https://github.com/vapor/fluent-mysql-driver.git", from: "4.0.0-rc")
    ],
    targets: [
        .target(
            name: "App",
            dependencies: [
                .product(name: "Fluent", package: "fluent"),
                .product(name: "FluentMySQLDriver", package: "fluent-mysql-driver"),
                .product(name: "Vapor", package: "vapor")
            ],
            swiftSettings: [
                .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
            ]
        ),
        .target(name: "Run", dependencies: [.target(name: "App")]),
        .testTarget(name: "AppTests", dependencies: [
            .target(name: "App"),
            .product(name: "XCTVapor", package: "vapor"),
        ])
    ]
)

Package.resolved:

  "object": {
    "pins": [
      {
        "package": "async-http-client",
        "repositoryURL": "https://github.com/swift-server/async-http-client.git",
        "state": {
          "branch": null,
          "revision": "037b70291941fe43de668066eb6fb802c5e181d2",
          "version": "1.1.1"
        }
      },
      {
        "package": "async-kit",
        "repositoryURL": "https://github.com/vapor/async-kit.git",
        "state": {
          "branch": null,
          "revision": "48a719dec79ea3cbbc71ca9b29f69df7cd5794cd",
          "version": "1.0.1"
        }
      },
      {
        "package": "console-kit",
        "repositoryURL": "https://github.com/vapor/console-kit.git",
        "state": {
          "branch": null,
          "revision": "7a97a5ea7fefe61cf2c943242113125b0f396a98",
          "version": "4.1.0"
        }
      },
      {
        "package": "fluent",
        "repositoryURL": "https://github.com/vapor/fluent.git",
        "state": {
          "branch": null,
          "revision": "dba22d5a6115092482669efe5492d800ace4da38",
          "version": "4.0.0-rc.2.2"
        }
      },
      {
        "package": "fluent-kit",
        "repositoryURL": "https://github.com/vapor/fluent-kit.git",
        "state": {
          "branch": null,
          "revision": "c73570c85d6661c04b8ea29a9f8eede67f6d4aae",
          "version": "1.0.0-rc.1.26"
        }
      },
      {
        "package": "fluent-mysql-driver",
        "repositoryURL": "https://github.com/vapor/fluent-mysql-driver.git",
        "state": {
          "branch": null,
          "revision": "74d95e80b998e3d143a0361cd0ecd8b43f2d9fd1",
          "version": "4.0.0-rc.1.3"
        }
      },
      {
        "package": "mysql-kit",
        "repositoryURL": "https://github.com/vapor/mysql-kit.git",
        "state": {
          "branch": null,
          "revision": "b59a08d77c3830b21f35fe744813640ef56a2ebb",
          "version": "4.0.0-rc.1.6"
        }
      },
      {
        "package": "mysql-nio",
        "repositoryURL": "https://github.com/vapor/mysql-nio.git",
        "state": {
          "branch": null,
          "revision": "6a9235582076122badc5428c8d5b6c41f9e40e1a",
          "version": "1.0.0-rc.2.2"
        }
      },
      {
        "package": "routing-kit",
        "repositoryURL": "https://github.com/vapor/routing-kit.git",
        "state": {
          "branch": null,
          "revision": "e7f2d5bd36dc65a9edb303541cb678515a7fece3",
          "version": "4.1.0"
        }
      },
      {
        "package": "sql-kit",
        "repositoryURL": "https://github.com/vapor/sql-kit.git",
        "state": {
          "branch": null,
          "revision": "1e0239616134dd45fa4894bfe83750d196e3a83d",
          "version": "3.0.0"
        }
      },
      {
        "package": "swift-backtrace",
        "repositoryURL": "https://github.com/swift-server/swift-backtrace.git",
        "state": {
          "branch": null,
          "revision": "f2fd8c4845a123419c348e0bc4b3839c414077d5",
          "version": "1.2.0"
        }
      },
      {
        "package": "swift-crypto",
        "repositoryURL": "https://github.com/apple/swift-crypto.git",
        "state": {
          "branch": null,
          "revision": "d67ac68d09a95443303e9d6e37b34e7ba101d5f1",
          "version": "1.0.1"
        }
      },
      {
        "package": "swift-log",
        "repositoryURL": "https://github.com/apple/swift-log.git",
        "state": {
          "branch": null,
          "revision": "74d7b91ceebc85daf387ebb206003f78813f71aa",
          "version": "1.2.0"
        }
      },
      {
        "package": "swift-metrics",
        "repositoryURL": "https://github.com/apple/swift-metrics.git",
        "state": {
          "branch": null,
          "revision": "708b960b4605abb20bc55d65abf6bad607252200",
          "version": "2.0.0"
        }
      },
      {
        "package": "swift-nio",
        "repositoryURL": "https://github.com/apple/swift-nio.git",
        "state": {
          "branch": null,
          "revision": "c5fa0b456524cd73dc3ddbb263d4f46c20b86ca3",
          "version": "2.17.0"
        }
      },
      {
        "package": "swift-nio-extras",
        "repositoryURL": "https://github.com/apple/swift-nio-extras.git",
        "state": {
          "branch": null,
          "revision": "f21a87da1353b97ab914d4e36599a09bd06e936b",
          "version": "1.5.0"
        }
      },
      {
        "package": "swift-nio-http2",
        "repositoryURL": "https://github.com/apple/swift-nio-http2.git",
        "state": {
          "branch": null,
          "revision": "b66a08e4bc53ab7c39fb03ab3678132e6ba5d12d",
          "version": "1.12.0"
        }
      },
      {
        "package": "swift-nio-ssl",
        "repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
        "state": {
          "branch": null,
          "revision": "10e0e17dd47b594c3d864a063f343d716e33e5c1",
          "version": "2.7.3"
        }
      },
      {
        "package": "vapor",
        "repositoryURL": "https://github.com/vapor/vapor.git",
        "state": {
          "branch": null,
          "revision": "74bbf36fe0378fafe5413762affe8db6b871a380",
          "version": "4.5.1"
        }
      },
      {
        "package": "websocket-kit",
        "repositoryURL": "https://github.com/vapor/websocket-kit.git",
        "state": {
          "branch": null,
          "revision": "021edd1ca55451ad15b3e84da6b4064e4b877b34",
          "version": "2.1.0"
        }
      }
    ]
  },
  "version": 1
}

LogMoment (the model I'm just testing a select from):

import Vapor

final class LogMoment: Model, Content {
    init() {}

    static let schema = "logMoments"

    @ID(custom: .id) var id: Int?
    @Field(key: "startedAt") var startedAt: Date?    
    @Field(key: "completedAt") var completedAt: Date?
    @Field(key: "allTickersCount") var allTickersCount: Int?
    @Field(key: "tickersProcessed") var tickersProcessed: Int?
    @Field(key: "tickersNotProcessed") var tickersNotProcessed: Int?
    @Field(key: "durationSeconds") var durationSeconds: Int?

    init(id: Int? = nil,
        startedAt: Date? = nil,
        completedAt: Date? = nil,
        allTickersCount: Int? = nil,
        tickersProcessed: Int? = nil,
        tickersNotProcessed: Int? = nil,
        durationSeconds: Int? = nil
) {
        self.id = id
        self.startedAt = startedAt
        self.completedAt = completedAt
        self.allTickersCount = allTickersCount
        self.tickersProcessed = tickersProcessed
        self.tickersNotProcessed = tickersNotProcessed
        self.durationSeconds = durationSeconds
    }
}

Controller I'm using (I recognize the class is TickerController, not Moment controller. I had the same issue with the TickerModel, so I wanted to test a different model):

struct TickerController {
    func index(req: Request) throws -> EventLoopFuture<[LogMoment]> {
        return LogMoment.query(on: req.db).limit(1).all()
    }
}

configure.swift:

import FluentMySQLDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {
    // uncomment to serve files from /Public folder
    // app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))

    app.databases.use(.mysql(
        hostname: Environment.get("DATABASE_HOST") ?? "****",
        username: Environment.get("DATABASE_USERNAME") ?? "****",
        password: Environment.get("DATABASE_PASSWORD") ?? "****",
        database: Environment.get("DATABASE_NAME") ?? "****",
        tlsConfiguration: .none
    ), as: .mysql)

//    app.migrations.add(CreateTodo())

    // register routes
    try routes(app)
    app.commands.use(MomentsCommand(), as: "moments")
}

I have tlsConfiguration: .none in there, because my of a SSL handshake error, when using .forClient()

Dockerfile:

# Build image
# ================================
FROM swift:5.2-bionic as build
WORKDIR /build

# First just resolve dependencies.
# This creates a cached layer that can be reused
# as long as your Package.swift/Package.resolved
# files do not change.
COPY ./Package.* ./
RUN swift package resolve

# Copy entire repo into container
COPY . .

# Compile with optimizations
RUN swift build --enable-test-discovery -c release

# ================================
# Run image
# ================================
FROM swift:5.2-bionic-slim

# Create a vapor user and group with /app as its home directory
RUN useradd --user-group --create-home --home-dir /app vapor

WORKDIR /app

# Copy build artifacts
COPY --from=build --chown=vapor:vapor /build/.build/release /app
# Uncomment the next line if you need to load resources from the `Public` directory
#COPY --from=build --chown=vapor:vapor /build/Public /app/Public

# Ensure all further commands run as the vapor user
USER vapor

# Start the Vapor service when the image is run, default to listening on 8080 in production environment 
ENTRYPOINT ["./Run"]
CMD ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"]

docker-compose.yml:


services:
   tickertracker:
     image: ****
     expose:
       - 8001
     ports:
       - "8001:8001"
     restart: always
     command: ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8001"]
     environment:
       VIRTUAL_HOST: ****
       DATABASE_HOST: ****
       DATABASE_USERNAME: ****
       DATABASE_PASSWORD: ****
       DATABASE_NAME: ****
     container_name: tickertracker_prod
networks:
  default:
    external:
      name: nginx-proxy

Note, I have the port set to 8001, because I have multiple others containers on the host, so I am using docker-compose.yml to configure the port manually (which is also the reason for the nginx-proxy

I have tried to dig into why the fatalError() is being called, but I realized quickly this is way over my head.

Nothing seems to be out of date (in terms of my Package.resolved).

Any ideas?

Any other information that I can provide?

Rep2 commented 4 years ago

Might be helpful https://forums.swift.org/t/fluent-connect-mysql-8-failure-how-to-solve/35939/14

tanner0101 commented 3 years ago

Hi @jaredanderton sorry I missed this issue initially. It's possible this has been fixed since you posted, so running swift package update might be enough.

If the issue persists, please run your app with --log trace and upload the full output leading up to the crash here. That will help me track it down. Thanks!

tanner0101 commented 3 years ago

Ah I see now MySQL Version: 5.6.37. This package requires MySQL 5.7 and up.

jaredanderton commented 3 years ago

@tanner0101 thanks for the follow up! I totally missed that I am running 5.7 on my local, and 5.6 on my server. Nice catch, and thanks for letting me know!