Readyset Documentation

repository·main·Indexed 26 days ago

https://github.com/readysettech/readyset

Readyset is a transparent database cache for Postgres and MySQL that provides in-memory key-value store performance by automatically syncing cached query results via the database's replication stream. Documentation includes guides on running benchmarks with Prometheus, using the Dante library's PatternBuilder to author SQL patterns, configuring TypeClass constraints and DialectSupport, and setting up infrastructure for Jepsen tests.

Tokens
21.2K
Snippets
45
Records
112
Agent score
90%

What's inside Readyset

  1. Overview of the readyset-util crate

    main
    The readyset-util crate provides miscellaneous utilities and extensions to the Rust standard library. It is designed to be a foundational dependency used across the entire Readyset workspace. To maintain its role at the root of the dependency chain, code in this crate must remain generally useful to any crate using the Rust standard library and is strictly prohibited from depending on any other crates within the Readyset workspace.
  2. Overview of Readyset query and testing tools

    main

    The readyset-tools directory provides a suite of utilities for querying Noria deployments and performing basic tests. The available tools include:

    • controller_request: Issues a set of basic controller requests to the current leader.
    • metrics_dump: Prints a current dump of the leader instance's metrics.
    • query_installer: Installs basic DDL, a table, and a SELECT * view.
    • view_checker: Issues a view query to a specific view.
    • failpoint: Toggles failpoint behavior within a controller.
  3. Emulate a PostgreSQL server with psql-srv

    main

    Use psql-srv to test new databases or caching layers by emulating a PostgreSQL server. It allows you to intercept PostgreSQL wire protocol messages (like QUERY, PARSE, DESCRIBE, BIND, or EXECUTE) and delegate their execution to your own custom logic.

    To use it, you must implement the Backend trait for your custom backend type and then use the run_backend function, passing an instance of your backend and a connection stream.

  4. Emulate a MySQL/MariaDB server with mysql-srv-rs

    main
    The mysql-srv-rs crate provides bindings for emulating a MySQL/MariaDB server. It is designed for testing new databases or caching layers by allowing existing applications to connect to your system as if it were a standard MySQL server. Instead of a real database engine, you delegate operations like QUERY, PREPARE, and EXECUTE to user-defined logic by implementing the MysqlShim trait.
  5. Write stateful property-based tests with proptest-stateful

    main
    Use proptest-stateful to extend property-based testing to stateful systems. Instead of testing single inputs, you define a sequence of operations. The framework generates these sequences, executes them, checks postconditions, and performs shrinking to find the minimal failing sequence of operations.
  6. Quickstart: Run a MySQL differential-testing session

    main

    To perform a self-contained MySQL differential-testing session against a local Readyset instance, follow these steps:

    1. Build the binaries: Ensure both readyset and readyset-dante-oracle are built with the antithesis_sdk/full feature.
    2. Prepare the database: Drop and recreate a fresh MySQL database.
    3. Start Readyset: Launch Readyset with all FEATURE_* flags enabled, pointing to your upstream MySQL instance.
    4. Run the Oracle: Execute readyset-dante-oracle using --readyset-mode to compare the Readyset instance against the upstream MySQL database.

    Note: Use --readyset-mode when testing a real Readyset instance. If testing a transparent proxy like SQP, omit this flag.

    cd public/
    
    # Build both binaries (once).
    cargo build --bin readyset --bin readyset-dante-oracle --features antithesis_sdk/full
    
    # Fresh database and storage.
    mysql -h 127.0.0.1 -P 3306 -u root -pnoria \
      -e "DROP DATABASE IF EXISTS cfuzz; CREATE DATABASE cfuzz;"
    rm -rf /tmp/rs-cfuzz.db
    
    # Start Readyset on port 3307. Every FEATURE_* must be on (see Feature flags).
    FEATURE_FULL_MATERIALIZATION=true FEATURE_MATERIALIZATION_PERSISTENCE=true \
    FEATURE_MIXED_COMPARISONS=true FEATURE_NON_BLOCKING_INDEX_BUILD=true \
    FEATURE_PAGINATION=true FEATURE_PLACEHOLDER_INLINING=true \
    FEATURE_POST_LOOKUP=true FEATURE_STRADDLED_JOINS=true FEATURE_TOPK=true \
    LOG_LEVEL=error target/debug/readyset \
      --upstream-db-url mysql://root:noria@127.0.0.1:3306/cfuzz \
      --address 0.0.0.0:3307 --storage-dir /tmp/rs-cfuzz.db \
      --cache-mode deep-then-shallow --query-caching in-request-path &
    sleep 8
    
    # Run the oracle against that Readyset.
    target/debug/readyset-dante-oracle \
      --readyset-mode \
      --compare-to mysql://root:noria@127.0.0.1:3306/cfuzz \
      --readyset-url mysql://root:noria@127.0.0.1:3307/cfuzz \
      --seed 42 --max-queries 200 --rows-per-table 50
  7. Use model state to prevent invalid operations

    main

    To avoid testing known invalid states (like integer underflow), maintain a model_count in your TestState. Use next_state to track the expected value, op_generators to filter available operations, and preconditions_met to ensure the framework only attempts valid operations.

    // 1. Update state to track model
    struct TestState {
        model_count: usize,
    }
    
    // 2. Update model in next_state
    fn next_state(&mut self, op: &Self::Operation) {
        match op {
            CounterOp::Inc => self.model_count += 1,
            CounterOp::Dec => self.model_count -= 1,
        }
    }
    
    // 3. Use model to filter generators
    fn op_generators(&self) -> Vec<Self::OperationStrategy> {
        let mut ops = vec![Just(CounterOp::Inc).boxed()];
        if self.model_count > 0 {
            ops.push(Just(CounterOp::Dec).boxed());
        }
        ops
    }
    
    // 4. Use model for preconditions
    fn preconditions_met(&self, op: &Self::Operation) -> bool {
        match op {
            CounterOp::Inc => true,
            CounterOp::Dec => self.model_count > 0,
        }
    }
  8. Set up infrastructure for Jepsen tests

    main

    Jepsen tests for Readyset require a cluster of nodes to verify eventual consistency and liveness guarantees. To test a full high-availability configuration, you need a minimum of 6 nodes:

    • 1x Load Balancer
    • 1x Upstream DB
    • 1x Consul
    • 1x readyset-server
    • 2x Readyset adapters

    Infrastructure Requirements:

    • OS: Ubuntu Server (Debian is also likely compatible).
    • Hardware: Instances should be large enough to compile Readyset binaries in release mode (e.g., AWS c4.2xlarge).
    • Storage: Approximately 100GB of disk space for the root volume.
    • Networking: Nodes must be able to communicate over all ports. If not using Tailscale, ensure ports 22 (SSH), 5432 (PostgreSQL), and 8500 (Consul) are open from your control machine to each node.
  9. Capture reproduction scripts with --dump-repro

    main

    Use --dump-repro <path> to stream every DDL, INSERT, and SELECT in execution order to a file. This file is a self-contained replay script with inlined parameters, making it directly executable against mysql or psql.

    Note: DROP TABLE IF EXISTS is not recorded to ensure seed data is preserved during replay.

    target/debug/readyset-dante-oracle ... --dump-repro /tmp/repro.sql
    
    # Replay against a clean MySQL:
    mysql -h 127.0.0.1 -P 3306 -u root -pnoria scratch < /tmp/repro.sql
  10. Build and run Readyset from source

    main

    Follow these steps to build and run Readyset locally using Docker for the backing database.

    1. Clone the repository:

      git clone https://github.com/readysettech/readyset.git
      cd readyset
    2. Start the backing databases: Use Docker Compose to start Postgres and MySQL containers:

      docker-compose up -d

      (Note: You can edit docker-compose.yml to comment out the mysql or postgres fields if you only want to run one.)

    3. Compile and run Readyset: Replace <deployment name> with a unique identifier. This runs the Server and Adapter as a single process.

      Against Postgres:

      cargo run --bin readyset --release -- --database-type=postgresql --upstream-db-url=postgresql://postgres:readyset@127.0.0.1:5432/testdb  --address=0.0.0.0:5433 --deployment=<deployment name> --prometheus-metrics

      Against MySQL:

      cargo run --bin readyset --release -- --database-type=mysql --upstream-db-url=mysql://root:readyset@127.0.0.1:3306/testdb --address=0.0.0.0:3307 --deployment=<deployment name> --prometheus-metrics
    4. Connect to the database shell: Once running, you can connect via the following commands:

      Postgres:

      PGPASSWORD=readyset psql --host=127.0.0.1 --port=5433 --username=postgres --dbname=testdb

      MySQL:

      mysql --host=127.0.0.1 --port=3307 --user=root --password=readyset --database=testdb