Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e105a49
Optimize assertValidName by replacing regex with character-by-charact…
Dec 11, 2025
cadb12a
Phase 1: Add FastBuilder scaffolding with scalar type support
Dec 6, 2025
0f81a9c
Phase 2: Add directive support with type reference resolution
Dec 6, 2025
e39bf20
Phase 3: Add enumeration type support
Dec 6, 2025
e473ca8
Phase 4: Add input object type support with type reference resolution
Dec 6, 2025
d09a763
Phase 5: Add applied directive support with type reference resolution
Dec 6, 2025
798c7e1
Phase 6: Object Types - FastBuilder implementation
Dec 6, 2025
ea5d3cc
Phase 7: Interface Types - FastBuilder implementation
Dec 6, 2025
4472f7c
Phase 8: Union Types - FastBuilder implementation
Dec 6, 2025
e12c074
Phase 9: Validation and Edge Cases - FastBuilder implementation
Dec 6, 2025
b7e9e46
Add FastSchemaGenerator and JMH benchmark infrastructure
Dec 20, 2025
388ab73
Address review comment, change a name
Jan 11, 2026
62815b1
Tighter type-bounds for GraphQLSchema.additionalTypes.
Jan 20, 2026
1c54eae
Remove detached-type detection: all types are additionalTypes
Jan 20, 2026
46e2e9b
Merge remote-tracking branch 'origin/master' into fastbuilder
andimarek Jan 22, 2026
7cf12cd
Remove phase separator comments from FastBuilder tests
andimarek Jan 22, 2026
c446304
Fix FastBuilder Javadoc comments
andimarek Jan 22, 2026
16a8fc2
Change FastBuilder.addType to accept GraphQLNamedType
andimarek Jan 22, 2026
d430e66
Fixes
Jan 23, 2026
a15baf6
Refactor built-in directive handling
Jan 25, 2026
eb8e24c
Fix withSchemaAppliedDirectives(Collection) not scanning for type ref…
andimarek Jan 28, 2026
86da667
Merge remote-tracking branch 'upstream/master' into fastbuilder
Jan 29, 2026
719f91f
Remove cruft
Jan 29, 2026
14a31e4
Improve FastBuilder documentation and add @NullMarked
andimarek Jan 29, 2026
cd32b5c
Remove redundant checks in assertValidName, mark param @Nullable
andimarek Jan 29, 2026
131f169
Promote FastSchemaGenerator to @ExperimentalApi with safer defaults
Jan 30, 2026
23c59ae
Add type resolver validation to FastBuilder
Jan 30, 2026
5bcba4a
Merge remote-tracking branch 'upstream/master' into fastbuilder
Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,15 @@ tasks.named('jmhJar') {
}
}

jmh {
if (project.hasProperty('jmhInclude')) {
includes = [project.property('jmhInclude')]
}
if (project.hasProperty('jmhProfilers')) {
profilers = [project.property('jmhProfilers')]
}
}


task extractWithoutGuava(type: Copy) {
from({ zipTree({ "build/libs/graphql-java-${project.version}.jar" }) }) {
Expand Down
54 changes: 54 additions & 0 deletions src/jmh/java/benchmark/BuildSchemaBenchmark.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package benchmark;

import graphql.schema.idl.FastSchemaGenerator;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;

import java.util.concurrent.TimeUnit;

@Warmup(iterations = 2, time = 5)
@Measurement(iterations = 3)
@Fork(2)
@State(Scope.Benchmark)
public class BuildSchemaBenchmark {

static String largeSDL = BenchmarkUtils.loadResource("large-schema-4.graphqls");

private TypeDefinitionRegistry registry;

@Setup
public void setup() {
// Parse SDL once before benchmarks run
registry = new SchemaParser().parse(largeSDL);
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void benchmarkBuildSchemaAvgTime(Blackhole blackhole) {
blackhole.consume(new SchemaGenerator().makeExecutableSchema(registry, RuntimeWiring.MOCKED_WIRING));
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void benchmarkBuildSchemaAvgTimeFast(Blackhole blackhole) {
blackhole.consume(new FastSchemaGenerator().makeExecutableSchema(
SchemaGenerator.Options.defaultOptions().withValidation(false),
registry,
RuntimeWiring.MOCKED_WIRING));
}
}
20 changes: 18 additions & 2 deletions src/jmh/java/benchmark/CreateSchemaBenchmark.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package benchmark;

import graphql.schema.GraphQLSchema;
import graphql.schema.idl.FastSchemaGenerator;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
Expand All @@ -21,7 +22,7 @@
@Fork(2)
public class CreateSchemaBenchmark {

static String largeSDL = BenchmarkUtils.loadResource("large-schema-3.graphqls");
static String largeSDL = BenchmarkUtils.loadResource("large-schema-4.graphqls");

@Benchmark
@BenchmarkMode(Mode.Throughput)
Expand All @@ -37,11 +38,26 @@ public void benchmarkLargeSchemaCreateAvgTime(Blackhole blackhole) {
blackhole.consume(createSchema(largeSDL));
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void benchmarkLargeSchemaCreateAvgTimeFast(Blackhole blackhole) {
blackhole.consume(createSchemaFast(largeSDL));
}

private static GraphQLSchema createSchema(String sdl) {
TypeDefinitionRegistry registry = new SchemaParser().parse(sdl);
return new SchemaGenerator().makeExecutableSchema(registry, RuntimeWiring.MOCKED_WIRING);
}

private static GraphQLSchema createSchemaFast(String sdl) {
TypeDefinitionRegistry registry = new SchemaParser().parse(sdl);
return new FastSchemaGenerator().makeExecutableSchema(
SchemaGenerator.Options.defaultOptions().withValidation(false),
registry,
RuntimeWiring.MOCKED_WIRING);
}

@SuppressWarnings("InfiniteLoopStatement")
/// make this a main method if you want to run it in JProfiler etc..
public static void mainXXX(String[] args) {
Expand All @@ -54,4 +70,4 @@ public static void mainXXX(String[] args) {
}
}
}
}
}
35 changes: 29 additions & 6 deletions src/main/java/graphql/Assert.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import java.util.Collection;
import java.util.function.Supplier;
import java.util.regex.Pattern;

import static java.lang.String.format;

Expand Down Expand Up @@ -224,8 +223,6 @@ public static void assertFalse(boolean condition, String msgFmt, Object arg1, Ob

private static final String invalidNameErrorMessage = "Name must be non-null, non-empty and match [_A-Za-z][_0-9A-Za-z]* - was '%s'";

private static final Pattern validNamePattern = Pattern.compile("[_A-Za-z][_0-9A-Za-z]*");

/**
* Validates that the Lexical token name matches the current spec.
* currently non null, non empty,
Expand All @@ -234,11 +231,37 @@ public static void assertFalse(boolean condition, String msgFmt, Object arg1, Ob
*
* @return the name if valid, or AssertException if invalid.
*/
public static String assertValidName(String name) {
if (name != null && !name.isEmpty() && validNamePattern.matcher(name).matches()) {
public static String assertValidName(@Nullable String name) {
if (name != null && isValidName(name)) {
return name;
}
return throwAssert(invalidNameErrorMessage, name);
return throwAssert(invalidNameErrorMessage, String.valueOf(name));
}

/**
* Fast character-by-character validation without regex.
* Checks if name matches [_A-Za-z][_0-9A-Za-z]*
*/
private static boolean isValidName(String name) {
if (name.isEmpty()) {
return false;
}

// First character must be [_A-Za-z]
char first = name.charAt(0);
if (!(first == '_' || (first >= 'A' && first <= 'Z') || (first >= 'a' && first <= 'z'))) {
return false;
}

// Remaining characters must be [_0-9A-Za-z]
for (int i = 1; i < name.length(); i++) {
char c = name.charAt(i);
if (!(c == '_' || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) {
return false;
}
}

return true;
}

private static <T> T throwAssert(String format, Object... args) {
Expand Down
Loading
Loading