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?