swift-serverless / aws-lambda-swift-sprinter

AWS Lambda Custom Runtime for Swift with swift-nio 2.0 support
Apache License 2.0
69 stars 6 forks source link

Database connection in server-less #26

Closed mihirpmehta closed 4 years ago

mihirpmehta commented 4 years ago

I am using this example aws server-less environment.

I am able to deploy the code with given example on aws lambda. Now to use this in our actual project I need to use postgres and redis with swift.

I am unable to find example where i can use standalone postgresql with server-side swift without using other server framework like Vapor.

Can you please help us in finding the right-way to integrate swift example with postgres and redis.

Thank you.

Andrea-Scuderi commented 4 years ago

Hi @mihirpmehta, I haven't tried to import Redis and Postgresql. I suggest to try to import the libraries you find here: https://swift.org/server/ The requirement to integrate a library is:

mihirpmehta commented 4 years ago

Thanks for your response. I tried to integrate https://github.com/IBM-Swift/Swift-Kuery with given example but getting runtime error

/var/task/HelloWorld: error while loading shared libraries: libpq.so.5: cannot open shared object file: No such file or directory

Here is my docker file

FROM swift:5.1.1 as builder

RUN apt-get -qq update && apt-get -q -y install \
    libssl-dev libicu-dev 

RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libpq-dev 

Will try to import libraries from https://swift.org/server/ and give it a try

Thanks

Andrea-Scuderi commented 4 years ago

@mihirpmehta If you add a library to the code, you need to copy them or in the Layer or with the Lambda. Look at the structure of the Docker folder, there is a text file called swift-shared-library.txt that contains all the libraries you need to add to your layer. I suggest creating your version of that file with the library you need. Once you add the library don't forget to upload the layer on AWS.

mihirpmehta commented 4 years ago

Thank you for your guidance. I am able to run the lambda with third party library now. Just one more query that i cannot resolve is, Can i run/test it in my local machine on terminal ? I am using Visual Code and Mac

Thanks

Andrea-Scuderi commented 4 years ago

It's possible to run the test by using make test_package . But you need to add your unit test in swift. You should be able test the lambda. Look also to the packages to have some idea on how to do it. You can run the test using visual studio code and the docker. The project has the configuration to support it. Look here on how to setup the environment: https://github.com/ianpartridge/vscode-remote-try-swift

mihirpmehta commented 4 years ago

Thanks. Will give it a try :)

Andrea-Scuderi commented 4 years ago

Redis: Here an example on how to use Redis with Swift Sprinter

Package.swift

import PackageDescription

let package = Package(
    name: "RedisDemo",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
        .package(url: "https://github.com/swift-sprinter/aws-lambda-swift-sprinter-nio-plugin", from: "1.0.0-alpha.3"),
        .package(url: "https://gitlab.com/mordil/swift-redi-stack.git", from: "1.0.0-alpha.5"),
        .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A starget can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "RedisDemo",
            dependencies: ["LambdaSwiftSprinterNioPlugin", "Logging", "RediStack"]),
        .testTarget(
            name: "RedisDemoTests",
            dependencies: ["RedisDemo"]),
    ]
)

Main.swift

import AsyncHTTPClient
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
import LambdaSwiftSprinter
import LambdaSwiftSprinterNioPlugin
import Logging
import NIO
import NIOFoundationCompat
import RediStack

struct Event: Codable {
    let key: String
    let value: String
}

struct Response: Codable {
    let value: String
}

enum LambdaError: Error {
    case redisConnectionFailed
}

let logger = Logger(label: "AWS.Lambda.Redis")
let elasticacheConfigEndpoint = "<your cluster>.euw1.cache.amazonaws.com"

let eventLoop = httpClient.eventLoopGroup.next()
let connection = try? RedisConnection.connect(
        to: try .makeAddressResolvingHost(elasticacheConfigEndpoint,
                                          port: RedisConnection.defaultPort),
        on: eventLoop
    ).wait()

let syncCodableNIOLambda: SyncCodableNIOLambda<Event, Response> = { (event, context) throws -> EventLoopFuture<Response> in

    guard let connection = connection else {
        throw LambdaError.redisConnectionFailed
    }

    let future = connection.set(event.key, to: event.value)
        .flatMap {
            return connection.get(event.key)
        }
        .map { content -> Response in
            return Response(value: content ?? "")
        }
    return future
}

do {

    let sprinter = try SprinterNIO()
    //Note amend this line in case if it's required to use a different lambda handler.
    sprinter.register(handler: "setGet", lambda: syncCodableNIOLambda)

    try sprinter.run()
} catch {
    logger.error("\(String(describing: error))")
}

Note I used Redis with AWS Elastic Cache

Requirements:

The full example is here: https://github.com/Andrea-Scuderi/aws-lambda-swift-sprinter/tree/feature/Redis

Andrea-Scuderi commented 4 years ago

Redis: Here an example on how to use PostgreSQL with Swift Sprinter: Package.swift

import PackageDescription

let package = Package(
    name: "PostgreSQLDemo",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
         .package(url: "https://github.com/swift-sprinter/aws-lambda-swift-sprinter-nio-plugin", from: "1.0.0-alpha.3"),
        .package(url: "https://github.com/vapor/postgres-nio.git", from: "1.0.0-alpha.1.6"),
        .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
        .target(
            name: "PostgreSQLDemo",
            dependencies: ["LambdaSwiftSprinterNioPlugin", "Logging", "PostgresNIO"]),
        .testTarget(
            name: "PostgreSQLDemoTests",
            dependencies: ["PostgreSQLDemo"]),
    ]
)

main.swift

import AsyncHTTPClient
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
import LambdaSwiftSprinter
import LambdaSwiftSprinterNioPlugin
import Logging
import NIO
import NIOFoundationCompat
import PostgresNIO

struct Event: Codable {
    let query: String
}

struct Response: Codable {
    let value: String
}

enum LambdaError: Error {
    case connectionFailed
}

let logger = Logger(label: "AWS.Lambda.Redis")
let endpoint = "<yourdb>.rds.amazonaws.com"
do {
    let eventLoop = httpClient.eventLoopGroup.next()
    let connection = try PostgresConnection.connect(
        to: try .makeAddressResolvingHost(endpoint,
                                          port: 5432),
        on: eventLoop
    ).wait()

    logger.error("after connection")

    try connection.authenticate(username: "<username>",
                                database: "<db>",
                                password: "<password>").wait()

    let syncCodableNIOLambda: SyncCodableNIOLambda<Event, Response> = { (event, context) throws -> EventLoopFuture<Response> in

        let future = connection.query(event.query).map { (rows) -> Response in
            return Response(value: "\(rows)")

        }
        return future
    }

    let sprinter = try SprinterNIO()
    //Note amend this line in case if it's required to use a different lambda handler.
    sprinter.register(handler: "query", lambda: syncCodableNIOLambda)

    try sprinter.run()
} catch {
    logger.error("\(String(describing: error))")
}

Note I used Redis with AWS RDS

Requirements:

The lambda must be attached to the VPC configured for Elastic Cache

https://github.com/Andrea-Scuderi/aws-lambda-swift-sprinter/tree/feature/PostgreSQLDemo

mihirpmehta commented 4 years ago

This is great help indeed. Thanks

Andrea-Scuderi commented 4 years ago

Added examples for Redis #32 and PostgreSQL #33