hit-box / hitbox

A high-performance caching framework suitable for single-machine and for distributed applications in Rust
MIT License
73 stars 6 forks source link

Declarative configuration #36

Open singulared opened 2 years ago

singulared commented 2 years ago

The main idea of this task is to add some mechanism that implements the ability to declaratively describe the hitbox configuration.

Main configuration entities:

Configuration manager should own configuration state and should have the ability to reload it on runtime.

Basic example of config file (discussable)

inbound:
  proto: HTTP
  port: 8080
  host: 0.0.0.0

backends:
  - redis:
      proto: redis
      host: 127.0.0.1
      port: 6379
      database: 0

upstreams:
  google:
    host: 8.8.8.8
    port: 8080

endpoints:
  index:
    upstream: "google"
    backends:
      - redis
    matchers:
      - matcher:
          path: "/index/"
      - matcher:
          method: GET
AndreyErmilov commented 2 years ago

For the https://api.github.com/repos/vmg/redcarpet/issues?state=closed

inbound:
  proto: HTTP
  port: 8080
  host: 0.0.0.0

backends:
  - redis:
      proto: redis
      host: 127.0.0.1
      port: 6379
      database: 0

upstreams:
  google:
    host: 8.8.8.8
    port: 8080
    endpoints:
      index:
        backends:
          - redis
        matchers:
          - matcher:
              path: "/index/"
          - matcher:
              method: GET
  github:
    host: api.github.com
    port: 80
    endpoints:
      issues:
        backends:
          - redis
        matchers:
          - matcher:
              path: "/repos/{user:string}/{repo:string}/issues/"
              method: GET
        keys:
          query:
            - type: enum
              name: state
              values:
                - closed
                - open
AndreyErmilov commented 2 years ago

About Settings

It seems that we need to separate two layers from each other. The first layer is the structures which are the internal representation of the hitbox-server settings. The second layer is the user configuration (yaml etc.). The layer with the user configuration is converted into the first layer, the internal representation of the settings. Both the first and second layers solve the same task, but each layer has its own specifics.

  1. First of all, we don't want to use the internal structures of the hitbox server because we don't want to know about them. And we don't want to change everything if we need to change the internal structures of the hitbox server.
  2. The second reason is that user configuration should be as simple as possible. We add groups, etc. to automate and reduce copying.

Internal HitboxServer state

struct Hitbox

struct UpstreamSettings

struct Backend

Couple lines of code


trait Backend {}

trait Matcher<T> {
    fn apply(&self, source: T) -> bool;
}

trait KeyExtractor<T> {
    fn extract(&self, source: T) -> String;
}

trait Request {}
trait Response {}

impl Request for HTTPRequest {

}

impl Request for GRPC {

}

struct Endpoint<Request, Response>
where
    Request: Request,
    Response: Response,
{
    name: String,
    backend: Vec<Box<dyn Backend>>,
    request: Vec<Box<dyn Matcher<Request>>>,
    response: Vec<Box<dyn Matcher<Response>>>,
    cache_key: Vec<Box<dyn KeyExtractor<Request>>>,
}

impl<Request, Response> Endpoint<Request, Response>
where
    Request: Request,
    Response: Response,
{

}
AndreyErmilov commented 2 years ago

TBD: @singulared

# hitbox server base settings
server:
  host: 127.0.0.1
  port: 8080
  proto: HTTP

# map of applications
upstreams:
  api:
    host: 127.0.0.1
    port: 8080
    proto: HTTP

# map of backends
backends:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
  inmemory:
    max_size: 1Gb

# map of cache policies
policies:
  cache_with_lock:
    cache: enabled
    lock: local
    stale: disabled
  non_cacheable:
    cache: disabled

# default settings
cache:
  ttl: 1min
  prefix: api
  version: 1
  backend: redis
  policies: cache_with_lock

# map of groups
groups:
  api:
    upstream: api
  sitemaps:
    upstream: api
    backend: inmemory
  exclude:
    upstream: api
    policy: non_cacheable

# list of all endpoint
endpoints:
  - path: "/clusters/{cluster_id:i32}/comments/"
    request:
      query:
        page:
          type: i32
        sort_by:
          type: enum
          variants:
            - date
            - clicks
      headers:
        x-source:
          type: enum
          variants:
            - ios
    response:
      headers:
        content-type: application/json
      status: 200
      if: "cluster.items"
    group: api
  - path: "/cluster/{cluster_id:i32}/"
    group: api
  - path: "/sitemaps/"
    group: sitemaps
singulared commented 2 years ago

@AndreyErmilov As I said in #38 we cat interpret all params as strings as I think. This will lead to some simplification of the configuration scheme.

singulared commented 1 year ago

@EveIsSim https://serde.rs/