Skip to content

Latest commit

 

History

History

4

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Fibonacci Go

Estimated time: 15 minutes

References:

In the following lab, we will create the same Fibonacci Serverless Function Using Go Lang Fn SDK.

A tiling with squares whose side lengths are successive Fibonacci numbers: 1, 1, 2, 3, 5, 8, 13 and 21.

Start Fn Server

From a terminal window Check the fn version

$ fn version
Client version is latest version: 0.6.13
Server version:  0.3.749

First let's start the Fn local Server in DEBUG mode. By default, the server starts on port 8080

$ fn start --log-level DEBUG
2022/02/14 22:22:15 ¡¡¡ 'fn start' should NOT be used for PRODUCTION !!! see https://github.com/fnproject/fn-helm/
time="2022-02-14T21:22:17Z" level=info msg="Setting log level to" fields.level=DEBUG
time="2022-02-14T21:22:17Z" level=info msg="Registering data store provider 'sql'"
time="2022-02-14T21:22:17Z" level=debug msg="creating new datastore" db=sqlite3
time="2022-02-14T21:22:17Z" level=debug msg="mysql does not support sqlite3"
time="2022-02-14T21:22:17Z" level=debug msg="postgres does not support sqlite3"
time="2022-02-14T21:22:17Z" level=debug msg="mysql does not support sqlite3"
time="2022-02-14T21:22:17Z" level=debug msg="postgres does not support sqlite3"
time="2022-02-14T21:22:17Z" level=info msg="Connecting to DB" url="sqlite3:///app/data/fn.db"
time="2022-02-14T21:22:17Z" level=info msg="datastore dialed" datastore=sqlite3 max_idle_connections=256 url="sqlite3:///app/data/fn.db"
time="2022-02-14T21:22:17Z" level=debug msg="mysql does not support sqlite3"
time="2022-02-14T21:22:17Z" level=debug msg="postgres does not support sqlite3"
time="2022-02-14T21:22:17Z" level=info msg="agent starting cfg={MinDockerVersion:17.10.0-ce ContainerLabelTag: DockerNetworks: DockerLoadFile: DisableUnprivilegedContainers:false FreezeIdle:50ms HotPoll:200ms HotLauncherTimeout:1h0m0s HotPullTimeout:10m0s HotStartTimeout:5s DetachedHeadRoom:6m0s MaxResponseSize:0 MaxHdrResponseSize:0 MaxLogSize:1048576 MaxTotalCPU:0 MaxTotalMemory:0 MaxFsSize:0 MaxPIDs:50 MaxOpenFiles:0xc4201cbd18 MaxLockedMemory:0xc4201cbd30 MaxPendingSignals:0xc4201cbd38 MaxMessageQueue:0xc4201cbd40 PreForkPoolSize:0 PreForkImage:busybox PreForkCmd:tail -f /dev/null PreForkUseOnce:0 PreForkNetworks: EnableNBResourceTracker:false MaxTmpFsInodes:0 DisableReadOnlyRootFs:false DisableDebugUserLogs:false IOFSEnableTmpfs:false EnableFDKDebugInfo:false IOFSAgentPath:/iofs IOFSMountRoot:/Users/nono/.fn/iofs IOFSOpts: ImageCleanMaxSize:0 ImageCleanExemptTags: ImageEnableVolume:false}"
time="2022-02-14T21:22:17Z" level=info msg="no docker auths from config files found (this is fine)" error="open /root/.dockercfg: no such file or directory"
time="2022-02-14T21:22:17Z" level=info msg="available memory" cgroup_limit=9223372036854771712 head_room=673241497 total_memory=6732414976
time="2022-02-14T21:22:17Z" level=info msg="ram reservations" avail_memory=6059173479
time="2022-02-14T21:22:17Z" level=info msg="available cpu" avail_cpu=6000 total_cpu=6000
time="2022-02-14T21:22:17Z" level=info msg="cpu reservations" cpu=6000
time="2022-02-14T21:22:17Z" level=info msg="\n        ______\n       / ____/___\n      / /_  / __ \\\n     / __/ / / / /\n    /_/   /_/ /_/\n"
time="2022-02-14T21:22:17Z" level=info msg="Fn serving on `:8080`" type=full version=0.3.749

Create Fn Application

In another terminal window, Create your first fn Application

$ fn create app goapp

Now initialize your go Fn application

$ fn init --runtime go --trigger http --name fibonaccigo

The function is created with a couple of files and directories

$ tree
.
├── README.md
└── fibonaccigo
    
    ├── func.go
    ├── func.yaml
    └── go.mod

1 directory, 4 files

Edit the func.go with the following content

package main

import (
 "context"
 "encoding/json"
 "io"
 "log"

 fdk "github.com/fnproject/fdk-go"
)

func main() {
 fdk.Handle(fdk.HandlerFunc(myHandler))
}

type Fib struct {
 Input int `json:"input"`;
 Output int `json:"output"`
}


func fibonacci(n int) int {
 first, second := 0, 1
 for i:=0;i<n;i++{
  first, second = second, first+second
         }
 return first
}

func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
 f := &Fib{Input: 10}
 json.NewDecoder(in).Decode(f)
 f.Output  = fibonacci(f.Input)
        log.Print("Inside Go Fibonacci function")
 json.NewEncoder(out).Encode(&f)
}

Build and Local Deployment

Deploy the function on the local running server using fn deploy

$ fn --verbose deploy --app goapp --local
Deploying fibonaccigo to app: goapp
Bumped to version 0.0.14
Using Container engine docker
Building image nelvadas/fibonaccigo:0.0.14
FN_REGISTRY:  nelvadas
Current Context:  default
[+] Building 5.2s (15/15) FINISHED
 => [internal] load build definition from Dockerfile333135114                                                                                                                             0.0s
 => => transferring dockerfile: 320B                                                                                                                                                      0.0s
 => [internal] load .dockerignore                                                                                                                                                         0.0s
 => => transferring context: 2B                                                                                                                                                           0.0s
 => [internal] load metadata for docker.io/fnproject/go:1.15                                                                                                                              1.2s
 => [internal] load metadata for docker.io/fnproject/go:1.15-dev                                                                                                                          1.2s
 => [auth] fnproject/go:pull token for registry-1.docker.io                                                                                                                               0.0s
 => [stage-1 1/3] FROM docker.io/fnproject/go:1.15@sha256:26a4fc320eb78f6dc0bd7b07921892ae1d63ca22baf0287f3fa6b7e92eeba43f                                                                0.0s
 => [internal] load build context                                                                                                                                                         0.0s
 => => transferring context: 627B                                                                                                                                                         0.0s
 => [build-stage 1/5] FROM docker.io/fnproject/go:1.15-dev@sha256:3f0cc5b217be4af2a6b845d1b5a1a72f686f7a8a2017cc4d4134b85e3f17ab5a                                                        0.0s
 => CACHED [build-stage 2/5] WORKDIR /function                                                                                                                                            0.0s
 => CACHED [build-stage 3/5] WORKDIR /go/src/func/                                                                                                                                        0.0s
 => [build-stage 4/5] COPY . .                                                                                                                                                            0.0s
 => [build-stage 5/5] RUN cd /go/src/func/ && go build -o func                                                                                                                            3.9s
 => CACHED [stage-1 2/3] WORKDIR /function                                                                                                                                                0.0s
 => CACHED [stage-1 3/3] COPY --from=build-stage /go/src/func/func /function/                                                                                                             0.0s
 => exporting to image                                                                                                                                                                    0.0s
 => => exporting layers                                                                                                                                                                   0.0s
 => => writing image sha256:8e578036a446106d23e5872ee1b5d9778b4cec5710095bad14f5c53b184b6a6e                                                                                              0.0s
 => => naming to docker.io/nelvadas/fibonaccigo:0.0.14                                                                                                                                    0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Updating function fibonaccigo using image nelvadas/fibonaccigo:0.0.14...
Successfully created trigger: fibonaccigo
Trigger Endpoint: http://localhost:8080/t/goapp/fibonaccigo

Inspect the function

  • Inspect
$ $ fn inspect function goapp fibonaccigo
{
 "annotations": {
  "fnproject.io/fn/invokeEndpoint": "http://localhost:8080/invoke/01FWH8ARWKNG8G00GZJ0000130"
 },
 "app_id": "01FWH896TYNG8G00GZJ000012Z",
 "created_at": "2022-02-22T17:40:41.491Z",
 "id": "01FWH8ARWKNG8G00GZJ0000130",
 "idle_timeout": 30,
 "image": "nelvadas/fibonaccigo:0.0.14",
 "memory": 128,
 "name": "fibonaccigo",
 "timeout": 30,
 "updated_at": "2022-02-22T18:16:28.508Z"
}

Invoke the function

  • with fn invoke
$ $ echo -n '{"input": 8 }' |fn invoke goapp fibonaccigo
{"input":8,"output":21}

Fn Performance

Let's run a benchmark on the existing serverless function with hey() For each function invocation, a docker container is created to serve the request.

Create an Body file for the benchmark; for this we want to get Fibonaci(100)

$ echo {"input": 8 } > data.json
$ cat data.json
{"input": 8 }

Now run 1000 requests with 100 cucurrent calls.

$ $ hey -n 1000 -c 100  -m POST -D data.json  http://localhost:8080/t/goapp/fibonaccigo

Summary:
  Total: 8.1749 secs
  Slowest: 7.2793 secs
  Fastest: 0.0049 secs
  Average: 0.7283 secs
  Requests/sec: 122.3263

  Total data: 24000 bytes
  Size/request: 24 bytes

Response time histogram:
  0.005 [1] |
  0.732 [778] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  1.460 [70] |■■■■
  2.187 [32] |■■
  2.915 [29] |■
  3.642 [14] |■
  4.370 [26] |■
  5.097 [7] |
  5.824 [27] |■
  6.552 [13] |■
  7.279 [3] |


Latency distribution:
  10% in 0.0134 secs
  25% in 0.0211 secs
  50% in 0.0907 secs
  75% in 0.5842 secs
  90% in 2.7195 secs
  95% in 4.7985 secs
  99% in 6.0195 secs

Details (average, fastest, slowest):
  DNS+dialup: 0.0007 secs, 0.0049 secs, 7.2793 secs
  DNS-lookup: 0.0002 secs, 0.0000 secs, 0.0027 secs
  req write: 0.0001 secs, 0.0000 secs, 0.0027 secs
  resp wait: 0.7274 secs, 0.0048 secs, 7.2696 secs
  resp read: 0.0001 secs, 0.0000 secs, 0.0007 secs

Status code distribution:
  [200] 1000 responses

In average we can comple 122.3263 req/s with a latency ~6.0195 for 99% of the requests. ( Average response time 0.7283 secs)

For this use case latency is lowest with GraalVM Native. Regarding average response time GraalVM Native function outperform the Go function.