Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/java-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ jobs:
run: chmod +x gradlew

- name: Build with Gradle
run: ./gradlew :java-client:build
run: ./gradlew :java-client:spotlessCheck :java-client:build
2 changes: 1 addition & 1 deletion .github/workflows/record-store.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ jobs:
run: chmod +x gradlew

- name: Build with Gradle
run: ./gradlew :record-store:build
run: ./gradlew :record-store:spotlessCheck :record-store:build
2 changes: 1 addition & 1 deletion .github/workflows/testcontainers-foundationdb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ jobs:
run: chmod +x gradlew

- name: Build with Gradle
run: ./gradlew :testcontainers-foundationdb:build
run: ./gradlew :testcontainers-foundationdb:spotlessCheck :testcontainers-foundationdb:build
13 changes: 0 additions & 13 deletions HEADER

This file was deleted.

6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,9 @@ To run your application:
```
./gradlew :record-store:run
```

To format:
```bash
gradle :record-store:spotlessApply :java-client:spotlessApply :testcontainers-foundationdb:spotlessApply
gradle :record-store:spotlessCheck :java-client:spotlessCheck :testcontainers-foundationdb:spotlessCheck
```
13 changes: 8 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
plugins {
id "com.github.hierynomus.license" version"0.15.0"
id "com.diffplug.spotless" version "5.8.2"
}

allprojects {
apply plugin: 'com.github.hierynomus.license'
apply plugin: 'com.diffplug.spotless'
repositories {
mavenCentral()
}
license {
header rootProject.file('HEADER')
include "**/*.java"
spotless {
java {
removeUnusedImports()
googleJavaFormat()
licenseHeaderFile "$rootDir/license.header"
}
}
}
ext {
Expand Down
2 changes: 2 additions & 0 deletions docs/_layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
<br>
<a href="/record-store/examples">Examples</a>
<br>
<a href="/record-store/managed-schema">Managed schema</a>
<br>
<a href="/record-store/qa">Q&A</a>
<br>
<br>
Expand Down
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ A light, multi-model, user-defined place for your data.
* [gRPC](https://grpc.io)
* *very experimental* [GraphQL](https://graphql.org)

* **Battery included**
* Additionally to `RecordSpaces`, the Record-Store is offering dedicated developer experiences, such as *Key-Value*

* **Scalable** We are based on the same tech behind [CloudKit](https://www.foundationdb.org/files/record-layer-paper.pdf) called the [Record Layer](https://github.com/foundationdb/fdb-record-layer/). CloudKit uses the Record Layer to host billions of independent databases. The name of this project itself is a tribute to the Record Layer as we are exposing the layer within a gRPC interface.

* **Transactional** We are running on top of [FoundationDB](https://www.foundationdb.org/). FoundationDB gives you the power of ACID transactions in a distributed database.
Expand Down
33 changes: 33 additions & 0 deletions docs/managed-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: Managed schemas
---

In addition to the `recordSpaces` which allow you to define your own schemas, you also have the possibility to use `managedSchemas`. They are offering their own APIs and gRPC endpoints with a fixed schemas provided by the Record-Store.

## Managed KeyValue

Do you need a Key/Value experience? You can easily use this dedicated gRPC endpoint:

```grpc
service ManagedKV {
rpc put(KeyValue) returns (EmptyResponse);
rpc delete(DeleteRequest) returns (EmptyResponse);
rpc scan(ScanRequest) returns (stream KeyValue);
}

message EmptyResponse {}

message DeleteRequest {
bytes key_to_delete = 1;
}

message ScanRequest {
bytes start_key = 1;
bytes end_key = 2;
}

message KeyValue {
bytes key = 1;
bytes value = 2;
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@
package fr.pierrezemb.recordstore.client;

import fr.pierrezemb.recordstore.proto.RecordStoreProtocol;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull;

public class RecordField {
@Nonnull
private final String fieldName;
@Nonnull private final String fieldName;

public RecordField(@NotNull String fieldName) {
this.fieldName = fieldName;
Expand Down Expand Up @@ -92,16 +90,19 @@ public RecordStoreProtocol.QueryFilterNode greaterThanOrEquals(float value) {
return createQueryFilter(RecordStoreProtocol.FilterOperation.GREATER_THAN_OR_EQUALS, value);
}

private RecordStoreProtocol.QueryFilterNode createQueryFilter(RecordStoreProtocol.FilterOperation fieldOperation, Object value) {
private RecordStoreProtocol.QueryFilterNode createQueryFilter(
RecordStoreProtocol.FilterOperation fieldOperation, Object value) {
return RecordStoreProtocol.QueryFilterNode.newBuilder()
.setFieldNode(createQueryFilterFieldNode(fieldOperation, value))
.build();
.setFieldNode(createQueryFilterFieldNode(fieldOperation, value))
.build();
}

private RecordStoreProtocol.QueryFilterFieldNode createQueryFilterFieldNode(RecordStoreProtocol.FilterOperation fieldOperation, Object value) {
RecordStoreProtocol.QueryFilterFieldNode.Builder builder = RecordStoreProtocol.QueryFilterFieldNode.newBuilder()
.setField(fieldName)
.setOperation(fieldOperation);
private RecordStoreProtocol.QueryFilterFieldNode createQueryFilterFieldNode(
RecordStoreProtocol.FilterOperation fieldOperation, Object value) {
RecordStoreProtocol.QueryFilterFieldNode.Builder builder =
RecordStoreProtocol.QueryFilterFieldNode.newBuilder()
.setField(fieldName)
.setOperation(fieldOperation);

if (value instanceof Integer) {
builder.setInt32Value((Integer) value);
Expand All @@ -121,5 +122,4 @@ private RecordStoreProtocol.QueryFilterFieldNode createQueryFilterFieldNode(Reco

return builder.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import fr.pierrezemb.recordstore.proto.SchemaServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
Expand All @@ -43,7 +42,8 @@ public class RecordStoreClient {
private final AdminServiceGrpc.AdminServiceFutureStub asyncAdminStub;
private final RecordServiceGrpc.RecordServiceBlockingStub syncRecordStub;

private RecordStoreClient(String tenant, String recordSpace, String address, String token) throws InterruptedException, ExecutionException, TimeoutException {
private RecordStoreClient(String tenant, String recordSpace, String address, String token)
throws InterruptedException, ExecutionException, TimeoutException {
this.tenant = tenant;
this.recordSpace = recordSpace;
this.address = address;
Expand All @@ -70,26 +70,30 @@ public ListenableFuture<RecordStoreProtocol.EmptyResponse> ping() {
return this.asyncAdminStub.ping(RecordStoreProtocol.EmptyRequest.newBuilder().build());
}

public ListenableFuture<RecordStoreProtocol.EmptyResponse> upsertSchema(RecordStoreProtocol.UpsertSchemaRequest request) {
public ListenableFuture<RecordStoreProtocol.EmptyResponse> upsertSchema(
RecordStoreProtocol.UpsertSchemaRequest request) {
return this.asyncSchemaStub.upsert(request);
}

public ListenableFuture<RecordStoreProtocol.EmptyResponse> putRecord(Message record) {
return this.putRecord(record.getClass().getSimpleName(), record.toByteArray());
}

public ListenableFuture<RecordStoreProtocol.EmptyResponse> putRecord(String recordTypeName, byte[] message) {
return this.asyncRecordStub.put(RecordStoreProtocol.PutRecordRequest.newBuilder()
.setMessage(ByteString.copyFrom(message))
.setRecordTypeName(recordTypeName)
.build());
public ListenableFuture<RecordStoreProtocol.EmptyResponse> putRecord(
String recordTypeName, byte[] message) {
return this.asyncRecordStub.put(
RecordStoreProtocol.PutRecordRequest.newBuilder()
.setMessage(ByteString.copyFrom(message))
.setRecordTypeName(recordTypeName)
.build());
}

public ListenableFuture<RecordStoreProtocol.StatResponse> getStats() {
return asyncSchemaStub.stat(RecordStoreProtocol.StatRequest.newBuilder().build());
}

public Iterator<RecordStoreProtocol.QueryResponse> queryRecords(RecordStoreProtocol.QueryRequest request) {
public Iterator<RecordStoreProtocol.QueryResponse> queryRecords(
RecordStoreProtocol.QueryRequest request) {
return syncRecordStub.query(request);
}

Expand Down Expand Up @@ -120,7 +124,8 @@ public Builder withToken(String token) {
return this;
}

public RecordStoreClient connect() throws InterruptedException, ExecutionException, TimeoutException {
public RecordStoreClient connect()
throws InterruptedException, ExecutionException, TimeoutException {
return new RecordStoreClient(tenant, recordSpace, address, token);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,72 +19,85 @@
import com.google.protobuf.Descriptors;
import fr.pierrezemb.recordstore.proto.RecordStoreProtocol;
import fr.pierrezemb.recordstore.utils.protobuf.ProtobufReflectionUtil;

import java.util.Collections;
import java.util.List;

/**
* A Utils class that can be used to easily generate UpsertSchema requests
*/
/** A Utils class that can be used to easily generate UpsertSchema requests */
public class SchemaUtils {
public static RecordStoreProtocol.UpsertSchemaRequest createSchemaRequest(
Descriptors.Descriptor descriptor,
List<RecordStoreProtocol.RecordTypeIndexDefinition> indexDefinitionList) {
Descriptors.Descriptor descriptor,
List<RecordStoreProtocol.RecordTypeIndexDefinition> indexDefinitionList) {

DescriptorProtos.FileDescriptorSet dependencies =
ProtobufReflectionUtil.protoFileDescriptorSet(descriptor);
ProtobufReflectionUtil.protoFileDescriptorSet(descriptor);

return RecordStoreProtocol.UpsertSchemaRequest.newBuilder()
.setSchema(dependencies)
.addAllRecordTypeIndexDefinitions(indexDefinitionList)
.build();
.setSchema(dependencies)
.addAllRecordTypeIndexDefinitions(indexDefinitionList)
.build();
}

public static RecordStoreProtocol.UpsertSchemaRequest createSchemaRequest(
Descriptors.Descriptor descriptor,
String recordTypeName,
String primaryKey,
String indexField, RecordStoreProtocol.IndexType indexType) {
return createSchemaRequest(descriptor, Collections.singletonList(
createIndex(recordTypeName, primaryKey, Collections.singletonList(createIndexDefinition(indexField, indexType)))
));
Descriptors.Descriptor descriptor,
String recordTypeName,
String primaryKey,
String indexField,
RecordStoreProtocol.IndexType indexType) {
return createSchemaRequest(
descriptor,
Collections.singletonList(
createIndex(
recordTypeName,
primaryKey,
Collections.singletonList(createIndexDefinition(indexField, indexType)))));
}

public static RecordStoreProtocol.UpsertSchemaRequest createSchemaRequest(
Descriptors.Descriptor descriptor,
RecordStoreProtocol.RecordTypeIndexDefinition indexDefinition) {
Descriptors.Descriptor descriptor,
RecordStoreProtocol.RecordTypeIndexDefinition indexDefinition) {
return createSchemaRequest(descriptor, Collections.singletonList(indexDefinition));
}

public static RecordStoreProtocol.RecordTypeIndexDefinition createIndex(String name, String primaryKeyField, RecordStoreProtocol.IndexDefinition indexDefinition) {
return createIndex(name, Collections.singletonList(primaryKeyField), Collections.singletonList(indexDefinition));
public static RecordStoreProtocol.RecordTypeIndexDefinition createIndex(
String name, String primaryKeyField, RecordStoreProtocol.IndexDefinition indexDefinition) {
return createIndex(
name,
Collections.singletonList(primaryKeyField),
Collections.singletonList(indexDefinition));
}

public static RecordStoreProtocol.RecordTypeIndexDefinition createIndex(String name, String primaryKeyField, List<RecordStoreProtocol.IndexDefinition> indexDefinitions) {
public static RecordStoreProtocol.RecordTypeIndexDefinition createIndex(
String name,
String primaryKeyField,
List<RecordStoreProtocol.IndexDefinition> indexDefinitions) {
return createIndex(name, Collections.singletonList(primaryKeyField), indexDefinitions);
}


public static RecordStoreProtocol.RecordTypeIndexDefinition createIndex(String name, List<String> primaryKeyFields, List<RecordStoreProtocol.IndexDefinition> indexDefinitions) {
public static RecordStoreProtocol.RecordTypeIndexDefinition createIndex(
String name,
List<String> primaryKeyFields,
List<RecordStoreProtocol.IndexDefinition> indexDefinitions) {
return RecordStoreProtocol.RecordTypeIndexDefinition.newBuilder()
.setName(name)
.addAllIndexDefinitions(indexDefinitions)
.addAllPrimaryKeyFields(primaryKeyFields)
.build();
.setName(name)
.addAllIndexDefinitions(indexDefinitions)
.addAllPrimaryKeyFields(primaryKeyFields)
.build();
}

public static RecordStoreProtocol.IndexDefinition createIndexDefinition(String field, RecordStoreProtocol.IndexType indexType) {
public static RecordStoreProtocol.IndexDefinition createIndexDefinition(
String field, RecordStoreProtocol.IndexType indexType) {
return RecordStoreProtocol.IndexDefinition.newBuilder()
.setField(field)
.setIndexType(indexType)
.build();
.setField(field)
.setIndexType(indexType)
.build();
}

public static RecordStoreProtocol.IndexDefinition createIndexDefinition(String field, RecordStoreProtocol.IndexType indexType, RecordStoreProtocol.FanType fanType) {
public static RecordStoreProtocol.IndexDefinition createIndexDefinition(
String field, RecordStoreProtocol.IndexType indexType, RecordStoreProtocol.FanType fanType) {
return RecordStoreProtocol.IndexDefinition.newBuilder()
.setField(field)
.setIndexType(indexType)
.setFanType(fanType)
.build();
.setField(field)
.setIndexType(indexType)
.setFanType(fanType)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static synchronized int nextFreePort() {
int port = nextPort++;
try (ServerSocket ss = new ServerSocket(port)) {
ss.close();
//Give it some time to truly close the connection
// Give it some time to truly close the connection
Thread.sleep(100);
return port;
} catch (Exception e) {
Expand Down
Loading