Skip to main content

ยท 2 min read
Florian Braun

Context and Problem Statement

We want a documentation page in which we provide documentation from different sources:

  • Manually written documentation
  • GoDoc generated documentation from go code
  • API Documentation from a swagger/openapi spec
  • ADRs are nicely presented

We want to have a nice page for it as when going public with this project, a good (single)entry point for people to look into the project is essential.

Decision Drivers

  • Having the ability to have different sources of documentation, generated and manually written ones.
  • Being able to statically deliver the documentation (e.g. via GitHub Pages)
  • Nice Look
  • Easy to write docs in
  • Being able to use Continuous Delivery to serve the docs.

Considered Options

Decision Outcome

Chosen option: "Docusaurus", because it was easy to use, produces very nice results, has the most features and seems to be widely used.

Pros and Cons of the Options

Docusaurus

  • Good, because it has a modern/nice look.
  • Good, because it has a plugin system which extends functions and delivers e.g. swagger/openapi support.
  • Good, because it is based on react and one is able to integrate own complex pages with that if one wants.
  • Good, because it has a build in deploy mechanism to deploy into a second branch on the same project.
  • Good, because it brings nice to have / advanced features for the future like versioned docs or multi-language support.

Jekyll

  • Good, because it is integrated into GitHub Enterprise. They will generate the page regularly for you.
  • Bad, because it has few functionality. Hard to get e.g. Swagger/OpenAPI Docs to Display.
  • Bad, because i diskliked the themes/page design in general.

mkdocs

  • Good, because it has a plugin system which extends functions and delivers e.g. swagger/openapi support.
  • Bad, because it doesn't offer an implementation of collectors to register multiple metrics at the same time
  • Bad, because i diskliked the themes/page design in general.

ยท 2 min read
Aftab Alam
Will Gant

Context and Problem Statement

We want our PoC to expose prometheus metrics to be consumed by a visualization dashbaord, e.g,. Grafana, and also to benchmark peformance against the exisitng Cloud Controller.

Decision Drivers

  • A metric system which can be easily extended with custom metrics to allow comparison with the performance of the existing Cloud Controller
  • Community support, solutions should be a broadly adapted technology.

Considered Options

Decision Outcome

Chosen option: "client_golang", because it the official and by far most popular library, and appears to have most features.

Pros and Cons of the Options

client_golang

  • Good, because it is the official library and has significant community support
  • Good, because it has all features we wanted and has advanced functionality, e.g,. sqlDBStats out-of-the-box, easily customizable...

VictoriaMetrics

  • Good, because it depends on one exeternal package as compared to eight for client_golang
  • Bad, because it has far fewer users and maintainers than client_golang
  • Bad, because it is missing "advanced funcationality" from client_golang (their words)
  • Bad, because it doesn't offer an implementation of collectors to register mulitple metrics at the same time

promenade

  • Bad, because it has client_golang as a dependency
  • Bad, because it has only one author and one user
  • Bad, because it doesn't offer an implementation of collectors to register mulitple metrics at the same time

ยท 3 min read
Andrew Paine
Sven Krieger
Philipp Thun

Context and Problem Statement

The existing Cloud Controller is the reference implementation of the CF v3 API. This project should avoid replacing the entire cloud_controller_ng project in a single "big bang" migration. In order to build the new implementation iteratively, this project should be deployable in parallel with the existing implementation. It should be possible to route individual API calls to the new Cloud Controller as soon as each endpoint is complete.

Decision Drivers

  • Discover bugs early
  • Deliver value from reimplementation quickly
  • Minimise mean-time-to-recovery when bugs are discovered

Considered Options

  1. Deploy new CC alongside existing CC in same instance group, using nginx for routing
  2. Complete an entire endpoint at once (all HTTP methods) and use gorouter for path based routing
  3. Deploy a dedicated path and HTTP method based router/proxy in front of old and new implementations and split traffic based on that

Decision Outcome

Chosen option: 3 (deploy a path and HTTP method based router/proxy in front of old and new implementations and split traffic based on that), because it is the only option that allows for separate scaling of old and new implementations as well as routing based on HTTP method and path. Following images shows a rough routing example with just the verry specific GET /v3/buildpacks/:guid endpoint beeing routed to the go implementation. Everything else will be routed to the cloudcontroller_ng. image

Positive Consequences

  • New implementation can be built in small units (endpoint + HTTP method)
  • Proxy can be registered only for certain routes, minimising throughput
  • Networking such as TLS in new implementation can be delayed until closer to completion (as proxy can perform this function)
  • Good support for HAProxy BOSH release as it is maintained by SAP team

Negative Consequences

  • Additional software to manage
  • HAProxy BOSH release is not that well suited to this use case

Pros and Cons of the Options

Option 1 (Deploy new CC alongside existing CC in same instance group, using nginx for routing)

  • Good, because does not change network architecture
  • Good, because does not add any new VMs
  • Bad, because cannot independently scale each implementation
  • Bad, because would require changes to the CAPI release to support this use case

Option 2 (Complete an entire endpoint at once (all HTTP methods) and use gorouter for path based routing)

  • Good, because does not change network architecture
  • Good, because can independently scale each implementation
  • Bad, because requires large amount of work to complete a whole endpoint
  • Bad, because requires new implementation to support TLS etc. for secure communication

ยท 2 min read
Florian Braun

Context and Problem Statement

We want our PoC to log various data in a structured way so that it is parsable by e.g. logsearch.

Decision Drivers

  • Fast logging (no wasted cpu time)
  • Customizable to make it compatible with the way log parsing in cloudfoundry works and maybe be able to mirror the log schema of the Cloud_Controller_NG if wanted.
  • Community support, solutions should be a broadly adapted technology.

Considered Options

Decision Outcome

Chosen option: "", because it has bradly used, very fast and has all features we want. As we started with zap, zerolog would be an alternative but we did not think it was worth it switching over as there was no obvious benefit. So we sticked to use zap.

Pros and Cons of the Options

logrus

  • Good, because has all features we like
  • Bad, because dead project, recommends alternatives
  • Bad, because bad performance

zap

  • Good, because very good performance,
  • Good, because broadly used
  • Good, because has all features we like

apex

  • Good, because has all features we like
  • Bad, because I dont like the interface
  • Bad, because it is not used by as much people as zap
  • Bad, because it is as slow as logrus

zerolog

  • Good, because its also very fast.
  • Good, because has all features we like
  • Good, because also broadly used but a bit less than zap

ยท 3 min read
Florian Braun
Marc Misoch
Andrew Paine

Context and Problem Statement

The existing Cloud Controller supports both Postgres and MySQL as a storage backend and both options are used in real production deployments today. In order to replace the existing Cloud Controller implementation, this project should also be able to support both

Decision Drivers

  • Community support: should be able to work with any CC DB
  • Performance: want a minimal runtime overhead
  • Performance: want control over SQL queries used to be able to optimise

Considered Options

  1. sqlboiler with separate packages for Postgres and MySQL
  2. sqlboiler with an extracted interface that is implemented by both Postgres and MySQL
  3. sqlboiler in a shared package with different build tags for each implementation
  4. GORM
  5. xo/xo

Decision Outcome

Chosen option: 3 (sqlboiler in a shared package with different build tags for each implementation) because this allows all of the model code to be generated from an existing schema and lets the compiler type check that the generated implementations have the same signatures without needing to manipulate the generated code much.

Positive Consequences

  • Model code can be regularly regenerated when CC DB schema changes (due to new migrations in existing implementation)
  • Smaller binaries as each only contains code relevant to that DB backend
  • Compile time checks that the generated code has the same function signatures for both implementations (or at least for all functions that are actually used)
  • Generated code can be easily extended to support optimisations

Negative Consequences

  • Extra tooling is required to rename and combine the files into a single package and add build tags
  • Directory containing generated files is extremely large
  • Developers need to supply build tags in order to browse, lint, compile and test code

Pros and Cons of other options

Option 1 (sqlboiler with separate packages for Postgres and MySQL)

  • Good, because sqlboiler can be run to generate the code without modification
  • Bad, because all controller code would need to do an if/switch statement on database type
  • Bad, because binaries will contain redundant code for other databases

Option 2 (sqlboiler with an extracted interface that is implemented by both Postgres and MySQL)

  • Good, because the interface could abstract between the two database
  • Good, because compiler would check that both generated implementations satisfy interface
  • Bad, because requires significant effort to extract complex sqlboiler interfaces
  • Bad, because some functions are static and cannot be extracted into an interface

Option 4 GORM

  • Good, because good documentation
  • Good, because controller code that interacts with database models is easy to write
  • Bad, because little/no support for generating models from existing schema
  • Bad, because has runtime overhead of using reflection

Option 5 xo/xo

  • Good, because generated models are extremely simple
  • Good, because templates are easy to customise
  • Bad, because generated models have no support for eager loading

ยท One min read
Andrew Paine
  • Status: accepted
  • Deciders: Andy Paine
  • Date: 2021-07-28

Context and Problem Statement

We want to record architectural decisions made in this project. Which format and structure should these records follow?

Considered Options

Decision Outcome

Chosen option: "MADR 2.1.2", because

  • Implicit assumptions should be made explicit. Design documentation is important to enable people understanding the decisions later on. See also A rational design process: How and why to fake it.
  • The MADR format is lean and fits our development style.
  • The MADR structure is comprehensible and facilitates usage & maintenance.
  • The MADR project is vivid.
  • Version 2.1.2 is the latest one available when starting to document ADRs.