This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Setup

Setup and configuration of client and server

This section will walk you through the many options of how to setup and operate FactCast.

1 - Prerequisites

Postgres

In order to run the FactCast server, you have to provide a Postgres database at least in version 9.4. The following example shows the configuration with one user.

spring.datasource.username="user" //that user has to be provided
spring.datasource.password="password"

The user has to be a superuser, as he will also install Postgres modules like uuid-ossp.

If you don’t want to provide a superuser

If you don’t want to provide a superuser, you have to consider the following points:

1.) The database user needs at least the permission to query the pg_roles view. According to the documentation it’s publicly accessible, so that shouldn’t be a problem.

2.) The FactCast needs the Postgres module uuid-ossp. You have to install that module manually. The server will recognize the already installed module and it won’t throw an error caused by missing privileges. Login into your Postgres console and execute the following command as superuser:

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

2 - fat-jar

Building

First of all, build factcast completely if not yet done by running

mvn install

In order to run a simple example FactCast server, you could enter the project factcast-examples/factcast-example-server and run

mvn spring-boot:run

or run it in your IDE. Note that it will use TestContainer to start an ephemeral postgres instance for you. That means, you need to have a runnable Docker installed on your machine.

In case you want to use your local Postgres instead, take a look at ExampleServerWithPostgresContainer to find out how what is necessary to use a pgsql. After all, this is just a very simple Spring Boot application using JDBC.

As expected, running

mvn package

will create a standard spring boot fat jar target/factcast.jar that can be run instantly.

Read more on Ports

3 - fat-jar (TLS)

Non-Encrypted

In order to run a simple example FactCast server, you could enter the project factcast-examples/factcast-example-server, see fatjar

TLS Server

There is an extra example project that demonstrates the usage of TLS for your server that can be found factcast-example-tls-server

We tried to stick as close as possible to what we have in factcast-examples/factcast-example-server to demonstrate the necessary changes and nothing more.

Obviously, for running a TLS Server, you need a certificate. We packaged a snakeoil localhost certificate for you to test. This cert can be found in src/etc/certificates/. In order to create your own selfsigned certificate, there is a shell script you can use as a starting point.

obviously, you should use proper trusted certificates when you run FactCast in production - you have been warned

In order to run the TLS Server, go to factcast-examples/factcast-example-tls-server and run

mvn spring-boot:run

Read more on Ports

4 - Ports

Port defaults and how to change them

The default TCP-Port exposed is 9090. As usual, you can set it via environment variables.

Standard ports used:

PortProtocolComponentProperty
9090HTTP2factcast-server-grpcgrpc.server.port (for the bind address: grpc.server.host, defaults to 0.0.0.0)

5 - Metrics

Being a regular Spring Boot 2+ application, the FactCast Server uses micrometer.io as its metrics emitting/collecting solution. In order to get started collecting the metrics FactCast Server emits, you’ll need to choose a backend/store for your metrics. Micrometer has lots of prebuilt bindings to choose from. Please refer to the respective documentation in the Setup section of the micrometer docs.

When it comes to metrics, you’ll have to know what you’re looking for. There are

  • Server metrics in FactCast Server as well as
  • Client metrics in the factcast client and additionally in the
  • factus client library.

We’re focussing on Server metrics here.

Metric namespaces and their organization

At the time of writing, there are six namespaces exposed:

  • factcast.server.timer
  • factcast.server.meter
  • factcast.store.timer
  • factcast.ui.timer
  • factcast.store.meter
  • factcast.registry.timer
  • factcast.registry.meter

Depending on your micrometer binding, you may see a slightly different spelling in your data (like ' factcast_store_timer`, if your datasource has a special meaning for the ‘.’-character)

Furthermore, metrics in operations are automatically tagged with

  • an operation name
  • a store name (‘pgsql’ currently) and
  • an exception tag (‘None’ if unset).

Existing metrics

There are a bunch of metrics already emitted in the server. These metrics can be grouped by type:

  • Timers (collecting durations of code execution)
  • Meters (collecting metric events, for example, occurrences of errors)

As this list is continuously growing, we cannot guarantee the documentation’s completeness. If you want to see the current list of operations, please look at StoreMetrics.java , RegistryMetrics.java , ServerMetrics.java, or UIMetrics.java respectively.

At the time of writing (0.4.3), the metrics exposed by the namespaces group factcast.server are:

operationtypedescription
handshaketimerDuration of the initial handshake.

At the time of writing (0.4.3), the metrics exposed by the namespaces group factcast.store are:

operationtypedescription
publishtimerTime to publish (write) a fact or a list of facts sent by the client.
Ref: concepts
subscribe-followtimerTime to create and return a follow subscription (not the actual stream of facts).
Ref: concepts
subscribe-catchuptimerTime to create and return a catchup subscription (not the actual stream of facts).
Ref: concepts
fetchByIdtimerTime to get a fact from a given ID.
serialOftimerTime to get the serial of a fact.
enumerateNamespacestimerTime to process namespaces enumeration.
enumerateTypestimerTime to process types enumeration.
getStateFortimerTime to get the latest state token for a given fact specification. The state represents the serial of the last fact matching the specifications, and is used by the client to determine whether a fact stream has been updated at a given point in time. Relevant for optimistic locking.
Ref: optimistic locking
publishIfUnchangedtimerTime to check against the given state token and possibly publish (write) a fact or a list of facts sent by the client.
Ref: optimistic locking
getSnapshottimerTime to read a snapshot from the cache.
Ref: snapshots
setSnapshottimerTime to create/update a snapshot from the cache.
Ref: snapshots
clearSnapshottimerTime to delete a snapshot from the cache.
Ref: snapshots
compactSnapshotCachetimerTime to delete old entries from the snapshot cache.
Ref: snapshots
invalidateStateTokentimerTime to invalidate the state token used for optimistic locking. The client can abort the transaction and let the server invalidate the token used for consistency.
Ref: optimistic locking
notifyRoundTripLatencytimerTime it takes for a notify on the database to be echoed back to the listener (roundtrip).
catchupFactmeterCounts the number of facts returned by a catchup subscription or catchup part of a follow subscription request (e.g. Factus managed projections) managed by the EventStore.
Ref: concepts
catchupTransformationRatiometer[deprecated] Percentage of facts transformed (downcasted/upcasted) by the server in response to a subscribed client. Useful for debugging the amount of overhead due to transforming, for subscription returning a significant amount of facts.
Ref: transformation
missedRoundtripmeterIf inactive for more than a configured interval (factcast.store.factNotificationBlockingWaitTimeInMillis), the server validates the health of the database connection. For this purpose it sends an internal notification to the database and waits to receive back an answer in the interval defined by factcast.store.factNotificationMaxRoundTripLatencyInMillis. This metric counts the number of notifications sent without an answer from the database.
snapshotsCompactedmeterCounts the number of old snapshots deleted. This runs as a dedicated scheduled job, configured by factcast.store.snapshotCacheCompactCron.
Ref: snapshots

At the time of writing (0.4.3), the metrics exposed by the namespaces group factcast.registry are:

operationtypedescription
transformEventtimerTime to transform (upcast/downcast) a single fact.
Ref: transformation
fetchRegistryFiletimerTime to retrieve a file from the schema registry.
Ref: facts validation
refreshRegistrytimerTime to execute the schema registry refresh, in order to get the latest schema and transformation updates.
compactTransformationCachetimerTime to delete old entries from the transformation cache.
transformationCache-hitmeterCounts the number of hits from the transformation cache.
transformationCache-missmeterCounts the number of misses from the transformation cache.
missingTransformationInformationmeterCounts the number of times that the server was not able to find transformation information from the schema registry.
transformationConflictmeterCounts the number of conflicts encountered by the server during schema registry update, which is caused by trying to change an existing transformation.
registryFileFetchFailedmeterCounts the number of times that the server was not able to get a json file from the schema registry.
schemaRegistryUnavailablemeterCounts the number of times that the server was unable to reach the schema registry.
transformationFailedmeterCounts the number of times that the server failed to transform a fact, using downcasting/upcasting scripts.
schemaConflictmeterCounts the number of conflicts detected by the server on the facts schema returned by the schema registry.
factValidationFailedmeterCounts the number of times that the server failed to validate a fact, that is attempted to be published, against the schema registry.
schemaMissingmeterCounts the number of times that the server detected a schema missing from the schema registry.
schemaUpdateFailuremeterCounts the number of times that the server was unable to update its schema definition from the schema registry, while fetching the initial state of the registry or during refresh.

gRPC Metrics

If you’re looking for remote calls and their execution times (including marshalling/de-marshalling from protobuf), you can have a look at the metrics automatically added by the gRPC library that we use. The relevant namespaces are:

  • grpcServerRequestsReceived and
  • grpcServerResponsesSent

These automatically added metrics only focus on service methods defined in the protocol buffer specs. Since a gRPC remote call triggers not everything we want to measure, we introduced additional metrics. When comparing, for instance, the automatically added durations of gRPC vs. the ‘factcast.store.duration’, you will find a subtle difference. The reason for this is that instead of including the gRPC overhead, we chose to only measure the actual invocations on the FactStore/TokenStore implementation. Depending on your needs, you may want to focus on one or the other.

Executor Metrics

Micrometer provides an integration to monitor the default thread pool executor created by Spring Boot. Under the same namespace executor.*, we publish metrics for our own thread pool executors used inside FactCast.

You can distinguish them by the name tag. Currently, these are:

  • subscription-factory - used for incoming new subscriptions
  • fetching-catchup - used for buffered transformation while using the fetching catchup strategy
  • paged-catchup - used for buffered transformation while using the paged catchup strategy
  • transformation-cache - used for inserting/updating entries in the transformation cache (only if you use persisted cache)

See https://micrometer.io/docs/ref/jvm for more information.

UI Metrics

Special metrics for the FactCast-Server UI are published via factcast.ui.timer namespace.

operationtypedescription
plugin-executiontimerTime to execute a specific plugin for one fact.
fact-processingtimerOverall time to process one fact. This includes execution of every plugin, parsing JSON payload and building the final representation model.

Additionally, all methods of the org.factcast.server.ui.adapter.FactRepositoryImpl are measured time-wise, and can be visualized via class and method dimension.

6 - Compression

Selecting a compressor

Why ?

Wherever there is network communication, the question of compression comes up. The FactCast server currently supports three compressors out of the box:

  • LZ4
  • Snappy
  • GZip

Unfortunately, GRPC does not support stream-compression, but only message-compression., This means that the efficiency of the compression is dependent on the message size. We’ll get to that…

Client chooses

In order to agree on which compressor to use, there is an initial handshake when the client connects to the server, in which the available compressors on client and server are compared, and the server selects the one to use.

The server send a list of what he accepts, and the client picks his favorite compressor out of that list in the order shown above (LZ4 first, then snappy, GZip as a fallback).

As the client should be low on dependencies and assumptions, Gzip (as supported by the JDK) is the default compressor every client supports.

In order to prefer snappy or LZ4, you’d need to add one or both of the following dependencies (or later versions) to your client. Once they are on the classpath, the client will pick them up automatically, and the server will prefer them over GZip.

Snappy

<dependency>
  <groupId>org.xerial.snappy</groupId>
  <artifactId>snappy-java</artifactId>
  <version>1.1.8.4</version>
</dependency>

LZ4

<dependency>
  <groupId>net.jpountz.lz4</groupId>
  <artifactId>lz4</artifactId>
  <version>1.3.0</version>
</dependency>

Compressor efficiency

As there currently is no stream-compression in GRPC, the server compresses each message transferred to the client separately. The smaller this message is, the less efficient the compression can be. For this reason it is important (during the catchup phase, where you expect a lot of messages) to allow the server to bundle messages into a batch that it will compress and send as one message.

See factcast.grpc.client.catchup-batchsize

In the follow phase, this setting has no meaning, as you don’t want to wait for your batch to fill up before you receive the latest publications from the server. Latency is more important than compression efficiency in that case.

7 - Security

Authentication & Authorization

In order to control access the FactCast Server supports a very basic way of defining which client is allowed to do what. The security feature is enabled by default, but can be disabled (for integration tests for example) by setting factcast.security.enabled to false.

In order to make use of the security features, a Bean of type FactCastAccessConfig must be defined. This is done either by providing one in your FactCast Server’s context, or by using the dead-simple approach to put a factcast-access.json on the root of your classpath or at /config/ to deserialize it from there.

Example below.

Now, that you’ve defined the access configuration, you also need to define the secrets for each account. Again, you can do that programmatically by providing a FactCastSecretsProperties, or by defining a property for each account like this:

factcast.access.secrets.brain=world
factcast.access.secrets.pinky=narf
factcast.access.secrets.snowball=grim

The catch with this simple approach of course is, that credentials are stored in plaintext in the server’s classpath, but remember it is just a dead-simple approach to get you started. Nobody says, that you cannot provide this information with a layer of your docker container, pull it from the AWS Parameter Store etc…

If FactCast misses a secret for a configured account on startup, it will stop immediately. On the other hand, if there is a secret defined for a non-existing account, this is just logged (WARNING-Level).

The contents of factcast-access.json might look like:

{
	"accounts": [
		{
			"id": "brain",
			"roles": ["anything"]
		},
		{
			"id": "pinky",
			"roles": ["anything", "limited"]
		},
		{
			"id": "snowball",
			"roles": ["readOnlyWithoutAudit"]
		}
	],
	"roles": [
		{
			"id": "anything",
			"read": {
				"include": ["*"]
			},
			"write": {
				"include": ["*"]
			}
		},
		{
			"id": "limited",
			"read": {
				"include": ["*"],
				"exclude": ["secret"]
			},
			"write": {
				"exclude": ["audit*"]
			}
		},
		{
			"id": "readOnlyWithoutAudit",
			"read": {
				"include": ["*"],
				"exclude": ["audit*", "secret"]
			},
			"write": {
				"exclude": ["*"]
			}
		}
	]
}

Where pinky & brain are authorized to use the full FactStore’s functionality (with ‘pinky’ not being able to write to namespaces that start with ‘audit’) whereas snowball can only read everything but ‘audit’-namespaces, but not write anything.

In case of conflicting information:

  • explicit wins over implicit
  • exclude wins over include

Note, there is no fancy wildcard handling other than a trailing ‘*’.

see module examples/factcast-example-server-basicauth for an example

Using BasicAuth from a client

From a client’s perspective, all you need to do is to provide credentials. Once the credentials are configured, they are used on every request in a Basic-Auth fashion (added header to request).

In order to define credentials, just set the appropriate property to a value of the format ‘username:password’, just as you would type them into your browser when a basic-auth popup appears.

# if this property is set with a value of the format 'username:password', basicauth will be used.
grpc.client.factstore.credentials=myUserName:mySecretPassword

You can always use environment variables or a -D switch in order to inject the credentials.

see module examples/factcast-example-client-basicauth for an example

Customizing Credential Loading

If you dont want to configure your passwords via properties, you can provide either a custom FactCastSecretProperties bean or an implementation of a UserDetailsService. That’s a simple interface coming from Spring Security which provides a mapping method from username to user. In our case we have to return a FactCastUser.

If you want to externalize secret loading but want to keep the factcast-access.json file for managing authorization an implementation of such a UserDetailsService could look like this:

@Bean
UserDetailsService userDetailsService(FactCastAccessConfiguration cc, PasswordEncoder passwordEncoder) {
    return username -> {
        // fetching account info from fact-access.json
        Optional<FactCastAccount> account = cc.findAccountById(username);

        // your way to fetch the user + password
        User user = loadUserByName(username);

        return account
            .map(acc -> new FactCastUser(acc, passwordEncoder.encode(user.getPassword())))
            .orElseThrow(() -> new UsernameNotFoundException(username));
    };
}

8 - gRPC Client

GRPC Clients

In order to talk to a - not in process - factstore (which is the usual setup for non-test applications), GRPC is the communication protocol used.

Using FactCast client in Spring boot via GRPC

If you use Spring take the easy path in your Spring Boot Application by adding the appropriate dependencies to your application:

   <dependency>
     <groupId>org.factcast</groupId>
     <artifactId>factcast-client-grpc</artifactId>
   </dependency>
   <dependency>
     <groupId>org.factcast</groupId>
     <artifactId>factcast-spring-boot-autoconfigure</artifactId>
   </dependency>

There are example projects: factcast-examples/factcast-example-client-spring-boot2 and factcast-examples/factcast-example-client-spring-boot1 respectively, that you can use as a template.

Note that factcast-client-grpc is built on top of (https://github.com/yidongnan/grpc-spring-boot-starter). If you are looking for the basic configuration properties, that is where you can find the latest version.

At the time of writing, the most relevant are:

NameExample Valuerequired
grpc.client.factstore.addressstatic://localhost:9090yes
grpc.client.factstore.negotiationTypePLAINTEXTno
grpc.client.factstore.enable-keep-alivetrueno

8.1 - gRPC BasicAuth

Using BasicAuth from a client

From a client’s perspective, all you need to do is to provide credentials. Once the credentials are configured, they are used on every request in a Basic-Auth fashion (added header to request).

In order to define credentials, just set the appropriate property to a value of the format ‘username:password’, just as you would type them into your browser when a basic-auth popup appears.

# if this property is set with a value of the format 'username:password', basicauth will be used.
grpc.client.factstore.credentials=myUserName:mySecretPassword

You can always use environment variables or a -D switch in order to inject the credentials.

see module examples/factcast-example-client-basicauth for an example

8.2 - gRPC KeepAlive

keep-alive settings

Here are some good settings for an initial configuration of a SpringBoot FactCast client/server setup in case you ran into gRPC related client server communication troubles.

  • Sending keep-alive HTTP/2 PINGs on the connection is useful in case you are running on infrastructure that doesn’t support configurable idle timeouts, and therefore closes connections.

  • The proposed values are defining a scenario where the client sends keep-alive HTTP/2 PINGs every 300s and the server accepts this behavior without sending GO_AWAY ENHANCE_YOUR_CALM to the client. Please adapt to your specific needs.

Client side

PropertyDescriptionRecommendedDefault
grpc.client.factstore.enable-keep-aliveConfigures whether keepAlive should be enabled.truefalse
grpc.client.factstore.keep-alive-timeThe default delay before sending keepAlives. Please note that shorter intervals increase the network burden for the server.30060
grpc.client.factstore.keep-alive-without-callsConfigures whether keepAlive will be performed when there are no outstanding RPCs on a connection.truefalse

Further details can be found here : net.devh.boot.grpc.client.config.GrpcChannelProperties.

Server side

PropertyDescriptionRecommendedDefault
grpc.server.permit-keep-alive-without-callsConfigures whether clients are allowed to send keep-alive HTTP/2 PINGs even if there are no outstanding RPCs on the connection.truefalse
grpc.server.permit-keep-alive-timeSpecifies the most aggressive keep-alive time in seconds clients are permitted to configure.100300

Further details can be found here : net.devh.boot.grpc.server.config.GrpcServerProperties.

8.3 - gRPC Resilience

Resilience approach

In order to make it easier for clients to deal with errors, we try to mitigate connection or network errors, or RetryableExceptions in general by just retrying. There are two types of gRPC communications within the FactCast gRPC API:

  • synchronous, request / response
  • asynchronous, request / streaming response

While the first can be mitigated easily by retrying the call, things get more complicated in an asynchronous, streaming scenario. Imagine a subscription to particular facts (let’s say 10) from scratch, where after 5 successfully received facts the network connection fails. Now simply retrying would mean to receive those 5 facts again, which is not only wasteful, but also hard to handle, as you’d need to skip those rather than process them a second time. Here the FactCast gRPC client keeps track of the facts successfully processed and resubscribes to the ones missing. In this example, it’ll try to subscribe to the same factstream but starting after the fifth fact.

Resilience is supposed to “just work” and let you deal with just the non-transient errors. This is why it is enabled by default with sane defaults.

If you want to disable it completely for any reason, you always can use

factcast.grpc.client.resilience.enabled=false

See properties for the defaults.

9 - Properties

Properties you can use to configure FactCast

Schemaregistry

PropertyDescriptionDefault
factcast.store.schemaRegistryUrlif a schemaRegistryUrl is defined, FactCast goes into validating mode. The only protocols allowed here are “http”, “https”, “classpath” and “file”. Note that http(s) and file always require two slashes after the colon, e.g. “https://someserver/…” or “file:///root/folder/…”.
factcast.store.validationEnabledCan be used for disabling Fact validation if a schemaRegistryUrl is defined. Useful for mass data ingestion.true
factcast.store.persistentRegistryif fetched Schema and Transformation Documents are persisted into Postgresfalse
factcast.store.allowUnvalidatedPublishIf validation is enabled, this controls if publishing facts, that are not validatable (due to missing meta-data or due to missing schema in the registry) are allowed to be published or should be rejected.false
factcast.store.schemaStoreRefreshCrondefines the cron schedule for refreshing the SchemaRegistry by querying for the latest remote changes*/60 * * * * * (every minute)
factcast.store.allowSchemaReplaceIf a schema can be replaced by an updated version from the registry (not a good idea in production environments)false

Transformation-Registry

PropertyDescriptionDefault
factcast.store.persistentTransformationCacheif Transformed Fact payloads are persistently cached into Postgresfalse
factcast.store.inMemTransformationCacheCapacitywhen using the inmem impl of the transformation cache, this is the max number of entries cached. The minimum value here is 100.100
factcast.store.deleteTransformationsStaleForDayswhen using the persistent impl of the transformation cache, this is the min number of days a transformation result is not read in order to be considered stale. This should free some space in a regular cleanup job. Must be a positive number.14
factcast.store.transformationCacheCompactCrondefines the cron schedule for compacting the transformation result cache0 0 0 * * * (at midnight)

Performance / Reliability

PropertyDescriptionDefault
factcast.store.factNotificationBlockingWaitTimeInMillisControls how long to block waiting for new notifications from the database (Postgres LISTEN/ NOTIFY mechanism). When this time exceeds the notifications is repeated. Minimum value is 5000.15000 (15sec)
factcast.store.factNotificationMaxRoundTripLatencyInMillisWhen FactCast did not receive any notifications after factNotificationBlockingWaitTimeInMillis milliseconds it validates the health of the database connection. For this purpose it sends an internal notification to the database and waits for the given time to receive back an answer. If the time is exceeded the database connection is renewed. Minimum value is 50.200
factcast.store.factNotificationNewConnectionWaitTimeInMillishow much time to wait between invalidating and acquiring a new connection. note: This parameter is only applied in the part of FactCast which deals with receiving and forwarding database notifications. Minimum value is 10.100
factcast.store.page-sizeHow many Facts to fetch from the database in one go. Higher values mean more memory usage. Must be positive.50
factcast.store.catchup-strategyFETCHING uses database cursors where PAGED uses separate queries on TEMPORARY tables. FETCHING tends to be faster.FETCHING
factcast.store.indexCheckCronCron expression defining a routine check for index validity0 0 3 * * * (3 am)
factcast.store.tailIndexingEnabledenable/ disable tail indexingfalse
factcast.store.tailManagementCroncron schedule when tail rotation should be carried out0 0 0 * * * (at midnight)
factcast.store.tailGenerationsToKeepthe number of tail indexes to keep. The higher the number, the slower the inserts. Probably 2 or 3 is a good value unless you have a very high tail rebuild frequency and not permanently connected applications (like offline clients for instance). Must be a positive number, maximum is 128.3
factcast.store.minimumTailAgeminimum age of the youngest tail index, before a new one is created7 days
factcast.store.tailCreationTimeoutIndex creation can hang for a long time in case of many open transactions. To avoid this, you can specify a timeout.
We will subtract 5 seconds from the given duration before applying it to setTimeout.
1d
factcast.store.transformationCachePageSizeDefines the max number of Facts being scheduled for transformation in one go. Must be positive and not exceed 32000.100
factcast.store.sizeOfThreadPoolForSubscriptionsThis is the number of threads we create for handling new subscriptions requests. It’s implemented via a fixed thread pool. As soon as the subscription request finishes or enters phase 3 (follow) the thread is freed up again. In earlier versions we used the common FJP which limits the parallelism to the number of cores - 1. If you ever encounter too much database load or too high waiting time for subscriptions this can be an option.100
factcast.store.sizeOfThreadPoolForBufferedTransformationsThis is the number of threads we create for handling buffered transformations. It’s implemented via work stealing thread pool. In early versions we used the common FJP which limits the parallelism to the number of cores - 1.25
factcast.store.readOnlyModeEnabledConfigures the FacCast to work in read-only mode. You cannot publish any events in this mode and certain functionality like tail index generation or state token generation is disabled. You can still use a persistent schema store or transformation cache, however they will work in read-only mode. Additionally, liquibase is disabled.false

Snapshots

PropertyDescriptionDefault
factcast.store.deleteSnapshotStaleForDaysmin number of days a snapshot is kept even though it is not read anymore. Must be a positive number.90
factcast.store.snapshotCacheCompactCrondefines the cron schedule for compacting the snapshot cache0 0 0 * * * (at midnight)

RedisSnapshots

PropertyDescriptionDefault
factcast.redis.deleteSnapshotStaleForDaysmin number of days a snapshot is kept even though it is not read anymore. Must be a positive number.90
factcast.redis.snapshotCacheRedissonCodecoptional configuration of the codec used for serializing objects from and into the snapshot. When set to RedissonDefault no codec is specified and Redisson will use its current default.MarshallingCodec

gRPC

Properties you can use to configure gRPC:

gRPC Client

PropertyDescriptionDefaultExample
grpc.client.factstore.credentialsCredentials in the form of username:secretnonemyUserName:mySecretPassword
grpc.client.factstore.addressthe address(es) fo the factcast servernonestatic://localhost:9090
grpc.client.factstore.negotiationTypeUsage of TLS or Plaintext?TLSPLAINTEXT
grpc.client.factstore.enable-keep-aliveConfigures whether keepAlive should be enabled. Recommended for long running (follow) subscriptionsfalsetrue
grpc.client.factstore.keep-alive-timeThe default delay before sending keepAlives. Defaults to 60s. Please note that shorter intervals increase the network burden for the server.300
grpc.client.factstore.keep-alive-without-callsConfigures whether keepAlive will be performed when there are no outstanding RPCs on a connection.falsetrue
grpc.client.factstore.enable-keep-alive=true
grpc.client.factstore.keep-alive-time=300
grpc.client.factstore.keep-alive-without-calls=true

Further details can be found here : net.devh.boot.grpc.client.config.GrpcChannelProperties.

FactCast client specific

PropertyDescriptionDefaultExample
factcast.grpc.client.idServer-side logging mentions this optional id if set in order to help with debugging. If this property is not set, it falls back to the value of spring.application.name${spring.application.name}myClient
factcast.grpc.client.catchup-batchsizeRequest a batchsize in catchup phase. Produces larger message and better compression. Remember that this setting increases the memory requirements, as well as the individual message size so depending on you Fact-payload size, and this setting, you may want to increase the allowed max-in/out limits of GRPC (defaulting to ~4mb per message). Our tests have shown that values >100 seem to have an insignificant impact - your mileage may vary. Setting is valid since 0.3.9.50100
factcast.grpc.client.enable-fast-forwardIf the server supports it, enables fast forwarding. This is supposed to speedup frequent queries that cluster around the end of the global Fact-Stream and thus can use dedicated temporary rolling indexes.truefalse
factcast.grpc.client.resilience.enabledEnables resilience mode for subscriptions. If enabled, subscriptions that fail due to networking errors will be transparently resubscribed. (since 0.5.5)truefalse
factcast.grpc.client.resilience.windowDefines the window in which a maximum of retries is defined. (since 0.5.5)PT30S (30 seconds)PT2M (2 Minutes)
factcast.grpc.client.resilience.attemptsDefines the maximum number of attempts that will be done (within a time window defined by resilience.window) before failing and escalating the last exception to the application. (since 0.5.5)1025
factcast.grpc.client.resilience.intervalDefines the wait time between two attempts. (since 0.5.5)PT0.1S (100 millis)PT0.5S
factcast.grpc.client.ignore-duplicate-factsIgnores and skips duplicate exceptions during publishing (does not include conditional publishing when using locks). This might be convenient in cases where you published to a factcast server and get a connection error back (you cannot possibly know if the publish on the server succeeded or not). If you have resilience enabled, the publish would be retried and might (if the first was successful) result in a DuplicateFactException. Setting this to true will make factcast just ignore the exception and go on. There might be a performance problem resulting from this: If you publish a batch of facts and a DuplicateFactException is recieved, factcast will fall back to publishing every single Fact from the batch one-by-one in order to make sure, that after your call, all Facts that are not duplicates will be published.falsetrue

grpc Server

PropertyDescriptionDefaultExample
grpc.server.permit-keep-alive-without-callsConfigures whether clients are allowed to send keep-alive HTTP/2 PINGs even if there are no outstanding RPCs on the connectionfalsetrue
grpc.server.permit-keep-alive-timeSpecifies the most aggressive keep-alive time in seconds clients are permitted to configure. Defaults to 5min.300100
factcast.grpc.bandwith.numberOfFollowRequestsAllowedPerClientPerMinuteafter the given number of follow requests from the same client per minute, subscriptions are rejected with RESOURCE_EXHAUSTED55
factcast.grpc.bandwith.initialNumberOfFollowRequestsAllowedPerClientramp-up to compensate for client startup5050
factcast.grpc.bandwith.numberOfCatchupRequestsAllowedPerClientPerMinuteafter the given number of catchup requests from the same client per minute, subscriptions are rejected with RESOURCE_EXHAUSTED60006000
factcast.grpc.bandwith.initialNumberOfCatchupRequestsAllowedPerClientramp-up to compensate for client startup3600036000
factcast.grpc.bandwith.disabledcompletely disables checking if set to truefalsetrue
grpc.server.permit-keep-alive-without-calls=true
grpc.server.permit-keep-alive-time=100

Blacklist

PropertyDescriptionDefaultExample
factcast.blacklist.typeConfigures where the list of blacklisted facts is retrieved from. One of [POSTGRES, RESOURCE].POSTGRES
factcast.blacklist.locationOnly required if type=RESOURCE. Specifies the URL where the JSON file containing the blacklist is located.classpath:blacklist.jsonfile:/some/path/blocked-facts.json

Testing

PropertyDescriptionDefault
factcast.store.integrationTestModewhen set to true, disables all non-essential memory-internal caches, timing might differ to production of course.false

UI

PropertyDescriptionDefault
vaadin.productionModeShould be set to true, otherwise vaadin tries to generate a dev bundle which is not necessary, and probably will fail.false

Further details can be found here : net.devh.boot.grpc.server.config.GrpcServerProperties.

10 - Docker

Building a FactCast server for integration testing using docker

Building

In order to build a standard docker container from source, enter the project factcast-docker and run

mvn docker:build

This will create a docker container as factcast/factcast and needs nothing more than the database URL to run.

Usage:

The docker container can be started

docker run -e"SPRING_DATASOURCE_URL=jdbc:postgresql://<POSTGRES-SERVER>/<DATABASENAME>?user=<USERNAME>&password=<PASSWORD>" -p 9090:9090 factcast/factcast

Note, that the resulting server is optimized and supposed to be used for integration testing only. Do not use it in production