Your production web stack in 2020

At Be Sport our stack is 100% Ocsigen (Lwt, js_of_ocaml, Eliom, Tyxml…)

Be Sport is a social network about sports (Web and mobile apps).

A few words about the stack we use:

  • The mobile (iOS and Android) and Web apps are written with one single multi-tier Eliom code (we use Ocsigen-Start as a basis). Lwt + js_of_ocaml
  • We use mainly postgresql with PGOcaml (even if we are not 100% happy with this solution). We use a pool a read-only db servers (currently with pgpool). We use Elasticsearch for our search engine.
  • Everything is hosted on AWS, with docker containers, and Elastic load balancer to dispatch to several instances of Ocsigen server (with SNS and SQS to dispatch notifications to all servers, and Firebase to send notification to mobile apps).
8 Likes

this is really helpful, it would be really appreciated to include some info on user base / requests per second/day, whether it is a commercial project and etc. to get a better understanding on whether the stack is battle tested;

also consider adding missing frameworks to techempower, so that we have a better visibility on the expected performance of different frameworks and stacks :slight_smile:

(code here: https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/OCaml)

4 Likes

@roddy, this looks really interesting for upstream cohttp. We’d definitely be up for merging this into the upstream library if you would consider submitting a PR. Cohttp’s Client is (deliberately) low-level HTTP, and the plan was always to have something higher-level that would handle issues of handling redirects and pipelining. Quests looks pretty much like it fits this bill at first glance.

4 Likes

Nothing against Quests as it looks like a fine library, but perhaps we should high levels on their own and focus on providing a solid foundation? There are other client libraries that look pretty good (resto comes to mind). It seems advantageous to keep cohttp as a shared & unified layer for various http types and allow various client implementations to be compatible. I can see plenty of other client implementations that may reuse these foundations; nsurl and curl based clients come to mind.

1 Like

We are using https://github.com/oxidizing/sihl/ for a small (10-20 routes) and a medium (~100 routes) sized project. We are going to use it for a larger project (~500 routes) and couple of smaller ones.

Someone mentioned Clojure’s Integrant, I think Sihl tries to do something similar.

It contains:

  • Database pooling and unified query interface: caqti (we don’t have an ORM so we have to manually implement repositories for MariaDB and Postgres since we want all Sihl services to support at least those two)
  • Migrations: custom service
  • Logging: logs
  • CLI Commands: custom service
  • Configuration: custom service
  • HTTP: opium (cohttp based with the API of the httpaf based one and some slight adjustments), custom middlewares
  • User management: custom service
  • Session: custom service
  • Queue: custom service
  • Email: letters for SMTP and custom for Sendgrid
  • Testing: alcotest
  • Block storage: custom SQL backend, S3 backend will follow soon
  • Scheduler: custom service
  • Schema validation: conformist
  • Templates: tyxml (jsx and functions)
  • Caching: not implemented
  • Concurrency: lwt

All the projects are deployed on a Docker Swarm infrastructure running behind Traefik.

We are quite happy with the stack.

10 Likes

@rubyblast, thanks for your reply.

When, you say:

I notice some like there type preserved from database storage to front-end, I agree so much on this!

could you give examples of techs and/or libs that would allow to do that.

I used Datomic + Datascript on my last project and was astonished on how much complexity disappears by having the exact same data format flowing from the database to the frontend.

Regards

1 Like

Ocsigen stack :slight_smile: Eliom to define shared types on both server and front (js_of_ocaml makes the magic happen behind) and js_of_ocaml. For free serialization/deserialazation from database I use jane street ppx (preprocessor) for S-expression (ie [@@deriving sexp]), and json equivalent ([@@deriving json] as it is required by eliom to make the type available on the front end transparently. I could use only that). However, other serialization ppx are available (I think yojson has one) for the same purpose, it is a matter of taste. Pgocaml gives also the ability to describe types with their serializer and deserializer to de/serialize transparently, but I just use de/serializations functions in my db calls.

Best !

1 Like

As @rubyblast mentions you have things like eliom which offer sophisticated support for that.

A more bare-bones approach is to be a bit careful with the packages you use in your data model and expose it as a library. Both your backend executable and your js_of_ocaml executable can then link against that library.

You then just need to define a serialisation format and corresponding codecs separately in your browser and in your server.

2 Likes

@Vincent_Balat what are your reservations regarding PGOCaml?

I think accessing the db at compile time is a wrong choice.

  • It makes it very difficult to package software or library (in opam for example).
  • And it does not give the guarantee we want on the compliance of the program w.r.t. the tables.
    This check should be done dynamically when launching the program.
9 Likes

Can you explain what is exactly the difficulty?

I suppose there is some basic check at compile time, such as “Connection to DB Ok?” and maybe “Programmed SQL requests (with empty tables) Ok?”.
Can you tell what check is done, and where it is programmed? (maybe just db-init target in Makefile.db?)
Thanks.

How do you push logs to datadog? I don’t see any OCaml API to push logs to datadog.
I looked at the ocaml-statsd-client but I don’t see the relationship with datadog.

Any plans on releasing this?

Caveat: I am no longer working for the same employer. For logs we could leverage the datadog AWS log forwarding integration as all of our services used the standard aws cloudwatch logging setup. The ocaml-statsd-client linked at GitHub - arenadotio/ocaml-statsd-client: Statsd client for OCaml was used for pushing metrics to datadog (via dogstatsd)

For a web backend: Linux, Cohttp, Lwt, directories of flat files to track the data.

For the web frontend: Js_of_ocaml.

This year I replaced the flat files with a real database: NDBM, mostly so that I wouldn’t have to spend an FS block size worth of disk for a minimum value store (though I probably could’ve looked into xfs or something).

I’ve also decided to dump Ubuntu and the GNU runtimes in favor of Alpine/musl with static linking, and also am doing pure-OCaml TLS with OCaml-based Let’s Encrypt provisioning.

Baby steps towards serving this entirely from Mirage one day.

2 Likes

@mbacarella do you happen to have a hint how to statically link linux/musl binaries x86_64/armv6l?

I used the alpine docker. Example

docker pull ocaml/opam:alpine-3.14-ocaml-4.12-flambda
# docker image ls and grab the image id
docker run -v "/home/mbac/projects/my_project:/tmp/my_project" -it $IMAGE_ID /bin/bash

inside the docker I did cd /tmp/my_project, installed all of my deps (e.g. opam install --deps-only .), then I built my package with dune. Be sure to add (flags (:standard -ccopt -static)) to your executable(s) stanza.

None of the other system build dependencies required this, but I had to specifically say apk add zlib-static

3 Likes

We have released (but not announced yet) a ppx that generates static CSS based on the Tailwind API: GitHub - hyper-systems/rescript-sx: Styling extension for ReScript.

It is likely that some utilities are missing right now, but adding them is straightforward.

We also built a small React components library that uses the this ppx, which I might open source in the future.

6 Likes