ParSeq Documentation

repository·master·Indexed 22 days ago

https://github.com/linkedin/parseq

A Java framework for simplifying asynchronous programming through primitives for task composition, parallelization, and error handling. Key features include non-blocking serialized execution, automatic I/O batching via BatchingStrategy, execution tracing with human-readable lambda names, and a specialized Rest.li client supporting transparent request aggregation and timeout configuration.

Tokens
5.2K
Snippets
18
Records
27
Agent score
77%

What's inside ParSeq

  1. Overview of ParSeq

    master

    ParSeq is a Java framework designed to simplify writing asynchronous code. It provides primitives for managing asynchronous operations, allowing developers to handle complex workflows with ease.

    Key capabilities include:

    • Parallelization: Running asynchronous operations (like IO) in parallel.
    • Serialized Execution: Performing non-blocking computations sequentially.
    • Task Composition: Reusing code by composing different tasks.
    • Error Handling: Simple propagation and recovery mechanisms.
    • Tracing: Execution tracing and visualization of task plans.
    • Batching: Support for batching asynchronous operations.
    • Retry Policies: Defining retry logic for tasks.
  2. Configure the ParSeq Rest.li Client

    master

    The ParSeq Rest.li Client provides two enhanced features over a standard Rest.li client: Batching and Configuration.

    Batching

    The client uses ParSeq Batching to transparently aggregate individual requests into BATCH requests.

    • Supported Operations: Currently only GET and BATCH_GET are supported.
    • Functionality: Batching can be selectively enabled for specific subsets of requests using configuration keys.

    Configuration Properties

    You can fine-grainly configure the following properties:

    • timeoutMs (long): Timeout in milliseconds. If the response is not available within this time, the returned Task completes with a TimeoutException (which includes details about the configuration that caused it).
    • batchingEnabled (boolean): Enables or disables batching for a specified subset of requests. Currently supports GET and BATCH_GET.
    • maxBatchSize (int): The maximum number of keys aggregated into a single BATCH request.
      • If maxBatchSize is 100 and there are 256 GET requests, they are grouped into three BATCH_GET requests (100, 100, 56).
      • Note: maxBatchSize does not split existing Rest.li BATCH_GET requests. If an existing BATCH_GET has 120 elements and maxBatchSize is 100, the 120-element request remains intact, and only subsequent individual GET requests are aggregated into batches of 100 and 20.
  3. Understand configuration key priority and specificity

    master

    When multiple configuration keys match a request, the key with the highest priority is used. Priority is determined by specificity.

    Priority Rules

    1. Resource name is more specific than operation type.
    2. Outbound resource is more specific than inbound resource.

    Deterministic Priority Scoring

    Each part of the key is assigned a priority score. A higher score indicates higher specificity. The structure follows this order of specificity (from most to least):

    <Outbound Resource Name>.<Outbound Operation>/<Inbound Resource Name>.<Inbound Operation>

    In score notation, this is represented as: <2>.<0>/<3>.<1>. This means the outbound resource name is the most specific, and the inbound operation type is the least specific.

    Example Priority Ordering

    Below is an example of keys sorted by priority (highest/most specific at the top):

    profileView.*/profile.FINDER-firstDegree
    *.*/profile.GET
    profileView.*/*.*
    *.*/*.GET
    *.*/*.*
  4. How ParSeq Batching works

    master

    ParSeq Batching automatically groups individual asynchronous operations into batches to improve efficiency, especially when dealing with I/O.

    Instead of executing every task immediately, the system identifies tasks that can be combined based on a defined strategy. When tasks are batched, their descriptions in traces are prefixed with batch:.

    Key benefits include:

    • Automatic Batching: Converts individual calls into batch calls without breaking modularity.
    • De-duplication: If multiple tasks in a batch request the same resource (e.g., the same ID), the batching mechanism can de-duplicate these requests so the underlying API is only called once for that resource.
    • Efficiency: Reduces the number of individual I/O operations by leveraging batch-capable APIs (like BATCH_GET).
  5. Task name format improvements in Parseq Lambda Names

    master

    Parseq Lambda Names transforms task names from opaque runtime identifiers into actionable debugging information.

    Standard Parseq behavior: Uses generated Lambda class names which are unique to a specific service instance but cannot be mapped back to source code. Example: andThen: com.linkedin.voyager.jobs.services.JobPostingsService$$Lambda$2760/928179328

    With Parseq Lambda Names: Provides the method name, the class name, the line number, and (where applicable) the function call within the lambda. Example: andThen: fetchJobPostings(JobPostingsService:112) Example with function inference: map: MapHelpers.mergeMaps(_,_) fetchTreatments(LixServiceImpl:124)

  6. Configure benchmark filters and iterations

    master

    You can fine-tune the benchmark execution by specifying regex filters for benchmark methods and configuring the number of forks, iterations, and warmup iterations.

    Commonly used flags:

    • -t <threads>: Number of worker threads.
    • -f <forks>: Number of forks.
    • -i <iterations>: Number of measurement iterations.
    • -wi <warmup_iterations>: Number of warmup iterations.
    • <regex>: A regular expression to filter which benchmark methods to run.

    Example: Running IdGeneratorBenchmark with 4 threads, 3 forks, 10 measurement iterations, and 5 warmup iterations:

    java -jar build/libs/benchmarks.jar -t 4 -f 3 -i 10 -wi 5 ".*IdGeneratorBenchmark.*"
  7. Set up ParSeq Batching

    master

    To enable batching in your application, you must register BatchingSupport as a PlanDeactivationListener to your Engine during setup. Then, register your custom BatchingStrategy with the BatchingSupport instance.

    // 1. Set up BatchingSupport in the Engine
    final BatchingSupport _batchingSupport = new BatchingSupport();
    engineBuilder.setPlanDeactivationListener(_batchingSupport);
    
    // 2. Register your strategy
    MyBatchingStrategy myBatchingStrategy = new MyBatchingStrategy();
    _batchingSupport.registerStrategy(myBatchingStrategy);
  8. Run the ParSeq Trace Visualizer

    master

    After building, you can run the visualizer by following these steps:

    1. Extract the parseq-tracevis.tar.gz package.
    2. Open trace.html in any web browser.

    Alternatively, the tool can be hosted from a web server. For development, coding, or debugging purposes, the visualizer can also be run directly from the directory containing this README.

    # Example workflow
    ./gradlew makeDist
    tar -xzf build/distributions/parseq-tracevis.tar.gz
    # Then open build/distributions/parseq-tracevis/trace.html in a browser
  9. How to use Parseq Lambda Names

    master

    Parseq Lambda Names improves Parseq traces by replacing runtime-generated, non-deterministic Lambda class names (e.g., com.linkedin...$$Lambda$123) with human-readable task names that include the source code location and function signatures.

    To enable this feature, include the shaded JAR of parseq-lambda-names on your classpath alongside the standard parseq JAR. When a Lambda is executed for the first time, the library will use ASM to analyze the bytecode and provide a meaningful description. If the parseq-lambda-names JAR is missing, Parseq will default to its standard behavior of using the generated Lambda class name.

    <!-- Ensure both JARs are on the classpath -->
    - parseq.jar
    - parseq-lambda-names-shaded.jar
  10. Build ParSeq

    master

    ParSeq uses Gradle for building.

    To build and test the entire project:

    ./gradlew clean build

    To build a specific subproject (module):

    ./gradlew :<module_name>:build

    MacOS Catalina (>=10.15) Setup: If you are building on MacOS Catalina or later, ensure you have installed the required Xcode Command Line Tools. You may also need to set the following environment variables:

    export LDFLAGS="-mmacosx-version-min=10.13"
    export CXXFLAGS="-mmacosx-version-min=10.13"
  11. Run ParSeq benchmarks

    master

    To run the benchmarks in the parseq-benchmark subproject, use Maven to build the project and then execute the generated JAR file. You can specify a regex pattern to filter specific benchmarks and set the number of threads using the -t flag.

    Note: The benchmark output uses JMH (Java Microbenchmark Harness) format, reporting results in operations per second (ops/s).

    # Build the benchmark project
    mvn clean install
    
    # Run benchmarks matching a regex pattern (e.g., ".*") with 4 threads
    java -jar target/benchmarks.jar ".*" -t 4