Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Java Backend Primitives

CI

A dependency-free Java 21 library of small backend building blocks for concurrency, streaming statistics, precise decimal math, and adaptive batch sizing.

The project favors explicit contracts, deterministic behavior, and testable lifecycle rules over framework-specific integrations.

Status

This project is currently a pre-release library. The source code is public and licensed under MIT, but the artifact is not yet published to Maven Central or another public package registry.

To use it today, clone the repository and install the artifact into your local Maven repository. The current coordinates are:

dev.azamir:java-backend-primitives:1.0.0-SNAPSHOT

Requirements

  • Java 21 or later
  • Maven 3.9 or later

Install locally

git clone https://github.com/azamir911/java-backend-primitives.git
cd java-backend-primitives
mvn clean install

This installs the library into your local Maven repository, normally under ~/.m2/repository.

Maven

After running mvn clean install, add:

<dependency>
  <groupId>dev.azamir</groupId>
  <artifactId>java-backend-primitives</artifactId>
  <version>1.0.0-SNAPSHOT</version>
</dependency>

Gradle

Use the local Maven repository:

repositories {
    mavenLocal()
}

dependencies {
    implementation("dev.azamir:java-backend-primitives:1.0.0-SNAPSHOT")
}

Components

Area Main types Purpose
Concurrent queues CloseableBlockingQueue, BoundedBlockingQueue, PollingBackoffQueue Bounded FIFO coordination with explicit shutdown behavior
Streaming statistics StreamingStatistics, StatisticsSnapshot One-pass descriptive statistics with thread-safe snapshots
Decimal ratios DecimalRatioCalculator Ratio calculations with explicit scale and rounding
Adaptive batching AdaptiveBatchSizer, RetryingSizeProbe Estimate safe batch sizes from serialized samples
Atomic accumulation ResettableLongAccumulator Lock-free accumulation with atomic snapshot and reset

Quick start

Bounded producer-consumer queue

import dev.azamir.primitives.concurrent.CloseableBlockingQueue;
import dev.azamir.primitives.concurrent.CloseableQueues;
import dev.azamir.primitives.concurrent.QueueClosedException;
import dev.azamir.primitives.concurrent.QueueWaitStrategy;

CloseableBlockingQueue<String> queue =
    CloseableQueues.bounded(128, QueueWaitStrategy.CONDITION_SIGNALING);

Thread producer = Thread.ofVirtual().start(() -> {
  try {
    queue.put("event-1");
    queue.put("event-2");
  } catch (InterruptedException interrupted) {
    Thread.currentThread().interrupt();
  } finally {
    queue.close();
  }
});

try {
  while (true) {
    System.out.println(queue.take());
  }
} catch (QueueClosedException drained) {
  // The queue was closed and all accepted elements were consumed.
}

producer.join();

Choose QueueWaitStrategy.POLLING_BACKOFF when polling behavior is intentionally required. The default condition-signaling implementation is more efficient for normal producer-consumer workloads.

Queue lifecycle

State put / offer take poll
Open with capacity Accepts an element Returns or waits Returns or times out
Open and full Waits or times out Returns an element Returns an element
Closed with buffered data Throws QueueClosedException Drains buffered data Drains buffered data
Closed and drained Throws QueueClosedException Throws QueueClosedException Returns empty

close() is idempotent and preserves buffered elements for draining. closeNow() closes the queue and discards buffered elements. clear() removes buffered elements without closing the queue.

All blocking methods propagate InterruptedException and preserve standard Java interruption semantics.

Streaming statistics

import dev.azamir.primitives.statistics.StatisticsSnapshot;
import dev.azamir.primitives.statistics.StreamingStatistics;

StreamingStatistics statistics = new StreamingStatistics();
statistics.add(12.5);
statistics.add(18.0);
statistics.add(14.5);

StatisticsSnapshot snapshot = statistics.snapshot();

long count = snapshot.count();
double sum = snapshot.sum();
double mean = snapshot.mean().orElseThrow();
double deviation = snapshot.sampleStandardDeviation().orElseThrow();

StreamingStatistics calculates count, sum, mean, sample variance, sample standard deviation, minimum, and maximum in one pass. It uses Welford's algorithm for stable online variance and compensated summation to reduce floating-point error.

Updates, snapshots, and resets are thread-safe.

Decimal ratio calculation

import dev.azamir.primitives.math.DecimalRatioCalculator;
import java.math.BigDecimal;
import java.math.RoundingMode;

DecimalRatioCalculator ratio =
    new DecimalRatioCalculator(new BigDecimal("0.10"));

BigDecimal result = ratio.calculate(
    new BigDecimal("2"),
    new BigDecimal("3"),
    2,
    RoundingMode.HALF_UP);

The constructor argument is an optional additive adjustment applied after division. Use BigDecimal.ZERO for a plain ratio.

The calculator validates zero denominators and invalid scales rather than relying on implicit arithmetic failures.

Adaptive batch sizing

import dev.azamir.primitives.batching.AdaptiveBatchSizer;
import dev.azamir.primitives.batching.RetryingSizeProbe;
import java.nio.charset.StandardCharsets;

byte[] serializedSample =
    "record-1,record-2".getBytes(StandardCharsets.UTF_8);
int sampledRecords = 2;
int headerBytes = 4;

RetryingSizeProbe<byte[]> probe =
    new RetryingSizeProbe<>(bytes -> bytes.length, 3);

AdaptiveBatchSizer sizer =
    new AdaptiveBatchSizer(
        1_048_576, // target serialized capacity
        0.85,      // safety factor
        1,         // minimum batch size
        100_000);  // maximum batch size

int recordsPerBatch = sizer.estimate(
    sampledRecords,
    serializedSample,
    probe,
    headerBytes);

The estimator separates fixed overhead from per-record payload, applies a safety factor, and clamps the result to configured minimum and maximum batch sizes.

RetryingSizeProbe<T> allows transient measurement or serialization failures to be retried within a fixed attempt budget while keeping the sizing algorithm independent from any serializer or framework.

Resettable atomic accumulator

import dev.azamir.primitives.concurrent.ResettableLongAccumulator;

ResettableLongAccumulator bytes = ResettableLongAccumulator.sum();
bytes.accumulate(1_024);
bytes.accumulate(2_048);

long intervalTotal = bytes.getAndReset(); // 3072

Custom associative operations are also supported. The supplied operation must be side-effect free because it may be retried during concurrent updates.

Build and test

mvn verify

The verification phase:

  • compiles the library with Java 21
  • runs unit tests
  • runs multi-threaded stress tests
  • creates a JaCoCo report under target/site/jacoco

Benchmarks

Build and run the JMH queue benchmark:

mvn -Pbenchmarks -DskipTests package
java -jar target/benchmarks.jar

The benchmark compares condition signaling and polling backoff. Results depend on the machine, JVM, queue capacity, wait strategy, and producer-consumer ratio. The repository includes a reproducible harness rather than publishing a hardware-independent performance claim.

Versioning and compatibility

The current 1.0.0-SNAPSHOT version is a development build. Public APIs may change before the first tagged stable release.

A stable release should use a non-SNAPSHOT version and be published to a public artifact registry before consumers rely on it as a remote dependency.

License

MIT

About

Dependency-free Java 21 primitives for concurrency, streaming statistics, precise decimal ratios, and adaptive batch sizing.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages