Docs / EventSourcingDB / Operations / Limits

Limits

This guide summarizes the technical and licensing limits that apply when using EventSourcingDB. It covers maximum capacities, storage considerations, and relevant operational constraints.

EventSourcingDB is designed to handle large volumes of events and subjects efficiently. However, some practical and licensing limits apply. This page provides an overview of these limits to support planning and system sizing.

Event Limits#

The number of events that EventSourcingDB can store is subject to both licensing and technical limits:

  • Licensing Limit: Evaluation licenses allow up to 25,000 events. Commercial licenses remove this restriction.
  • Technical Limit: The absolute maximum number of events is 9,223,372,036,854,775,807, corresponding to the largest value representable by a signed 64-bit integer.

In practice, the technical limit is extremely large and will not be reached under normal operational conditions. Most deployments are constrained by license terms and available disk space.

Subject Limits#

EventSourcingDB does not impose a strict technical limit on the number of subjects. Subjects are stored efficiently, and their number is practically bounded only by the total event volume and available storage.

Added in 1.3The indexes keep a small amount of memory for each subject, though, so a very large number of subjects also needs memory to match.

Event Type Limits#

EventSourcingDB does not impose a strict technical limit on the number of event types. Event types are managed efficiently, and their number is practically bounded only by the overall event volume and available storage.

Size Limits#

Changed in 1.3A single event may take at most 64 KiB, measured as it is stored, including its hash. A write that contains a larger event is rejected with event-too-large, an error item at status 200, and none of its events is stored. A restore whose backup holds such an event fails with the same error, and so does an import that holds one.

Added in 1.3A subject may have at most 32 segments and at most 1,024 characters, not counting a trailing slash, which the database removes, since the database keeps a subject together with every subject above it. The bounds hold for every subject that a request hands in, to write, read, or observe events, to list subjects, or in a precondition, and for the subject of every event of a backup to restore or of an import. A longer subject is rejected with subject-too-long, a deeper one with subject-too-deep, and the v1 API answers either with 400 Bad Request. Events that an earlier version stored with such a subject stay in the store and can still be read through a subject above it, through /, or with EventQL, but no longer by that subject itself, and a backup that holds one cannot be restored.

Added in 1.3Every number of an event schema, wherever it stands in the schema, may have at most 1,000 digits before its decimal point and at most 1,000 after it, counted as its exponent moves the point, so 1e-999999 has 999,999 digits after it. This is the bound to which EventQL keeps integers exact. Registering a schema with a number beyond it is rejected with event-schema-malformed, and the v1 API answers with 400 Bad Request. A restore whose backup holds such a schema fails with the same error.

Added in 1.3An event schema may reach at most 10,000 subschemas, counting a referenced one each time it is referenced, since a small schema that refers to the same definition more than once on each of many levels takes a time to validate an event against that doubles with every level. A schema that refers back to a part of itself, such as one that describes a tree, or that uses $dynamicRef or $recursiveRef to refer to an anchor, is refused however small it is. Registering such a schema is rejected with event-schema-too-complex, and the v1 API answers with 400 Bad Request. A restore whose backup holds such a schema fails with the same error. A schema of this kind that an earlier version registered stays in the store, but every write of its event type is rejected with the same error, which the v1 API answers with 409 Conflict.

The bodies of requests are limited as well:

  • Writing events accepts a body of up to 10 MiB, so that a single write can carry many events.
  • Changed in 1.3Reading and observing events, running EventQL queries, reading subjects, reading an event type, registering an event schema, auditing the history, and creating a backup accept a body of up to 100 KiB.
  • Restoring a backup accepts a body of any size, since the backup is streamed into the database.
  • Added in 1.3Importing events accepts a body of any size as well, since the events are streamed into the database.

Changed in 1.3A body that exceeds its limit is rejected with 413 Content Too Large. A body with a limit has to arrive within five seconds: one that takes longer is rejected with 408 Request Timeout, and one that breaks off before its end, for example because its client drops the connection, with 400 Bad Request. The body of a restore, which has no limit, may take as long as it needs, but its client must not pause for more than five seconds at a time; a restore whose body pauses longer or breaks off fails with backup-unreadable. The same holds for the body of an import, which fails with import-unreadable.

Added in 1.3A line of a backup to restore may hold at most 601 KiB, or 615,424 bytes, not counting its newline, which is more than any item of a backup takes: an event takes at most 64 KiB, and a schema at most 600 KiB as a backup writes it. That is six times the 100 KiB that registering a schema accepts, since a backup writes each <, >, and & in a string as an escape of six bytes. A restore refuses a longer line without reading it to its end, and likewise a schema that takes more than 600 KiB as a backup writes it, with backup-malformed; the v1 API answers either with 400 Bad Request. Only a backup that was changed by hand can bring such a schema into a store, since a restore takes a schema as the backup holds it, which may be far shorter than the next backup writes it. A schema that an earlier version took this way stays in the store, but no backup that holds it can be restored again.

A line of an import may hold at most 390 KiB, or 399,360 bytes, not counting its newline: six times the 64 KiB that an event takes at most as it is stored and the 1 KiB for the names of its fields, since JSON lets a client write any character as an escape of six bytes, such as \u0061 for a, and every event that the database stores imports however its characters are escaped. An import refuses a longer line without reading it to its end, with import-malformed, and an event that is larger than 64 KiB once its line is decoded with event-too-large. A line with much white space takes more than the event that it holds, so write a line compactly, as export-events does.

Storage Limits#

The storage capacity is primarily determined by the underlying file system and available disk space. EventSourcingDB itself imposes no internal maximum size on the event store. It keeps 1 GiB of the file system free, though, and refuses writes once less than that is left, as described in Read-Only Mode.

File System Constraints

Make sure the target file system supports large files and sufficient storage capacity, especially when managing millions of events over time.

Operational Limits#

When reading events via the HTTP API, EventSourcingDB streams the result continuously. There is no internal limit on the number of events returned. In principle, the entire event history can be streamed in a single request.

If a network interruption occurs during streaming, clients can resume reading from the last successfully received event by using the lowerBound parameter. For more information on how to resume event streams, see Reading Events.

Changed in 1.3A stream waits up to 60 seconds for its client to take in each item, and up to 10 seconds for it to take in the end of the stream after its last item. A client that stops reading for longer, for example because it processes a single event for that long, loses the connection. Every other answer of the routes of EventSourcingDB waits up to 10 seconds for its client to take it in as a whole, and a client that has not taken it in by then loses the connection as well. The same bound holds for the answers that EventSourcingDB gives on its own to malformed requests, before any route sees them: a 400 for a malformed request line or header, a 431 for headers that are too large, a 501 for an unknown transfer encoding, a 505 for an unsupported version of HTTP, and a 417 for an unknown expectation. It holds as well for the interim answer 100 Continue that EventSourcingDB sends over HTTP/1.1 to a request with Expect: 100-continue as it begins to read its body: a client that has not taken it in within 10 seconds loses the connection. The files of the management UI are bounded for each piece of up to 32 KiB instead: a client has to take in each piece within 10 seconds, rather than the file as a whole, so that a client on a slow link still gets a large file. Over HTTP/2, which the HTTPS listener speaks by default, all answers on a connection share it, so a connection on which a write waits 60 seconds for its client, as long as a stream waits for its client to take in an item, is closed together with every request on it, even one whose own bound has not passed yet.

Changed in 1.3An EventQL query holds at most 1,000,000 rows at once in each part that holds rows in memory, such as an ORDER BY that sorts them, and at most 1 GiB in all of these parts together, by an estimate of their size, and fails at whichever limit it reaches first, as described in Rows Held in Memory. The values that DISTINCT, MEDIAN, STDDEV, and VARIANCE keep count towards both limits as well, a string that a query builds may hold at most 1 MiB, and an array or an object that it builds at most 64 MiB, by the same estimate, which counts a value each time the array or the object holds it.

A query may nest at most 256 levels, and a chain of operators in it may hold at most 10,000 operands, as described in the Syntax Overview. Both are counted before the query is parsed, wherever it is parsed: to run it, in an isEventQlQueryTrue precondition, and to format it in the management UI. A query beyond either bound is rejected with query-too-deep, and the v1 API answers with 400 Bad Request.

The query of an isEventQlQueryTrue precondition may have at most 100 KiB, or 102,400 bytes, as much as the body of running a query on its own takes, although it arrives in the body of a write, which may take up to 10 MiB. A longer one is rejected before it is parsed, and before the write waits for the other writes, with query-too-long, and the v1 API answers with 400 Bad Request.

Added in 1.3A restore takes an event that lies at most an hour beyond the clock of the server. A backup with an event further ahead, such as one dated 9999-12-31, is rejected with backup-ahead-of-clock, and the v1 API answers with 400 Bad Request; the same backup restores once the clock has caught up. An import takes an event within the same hour, and refuses one further ahead with import-ahead-of-clock. An event can be dated no earlier than 1970-01-01T00:00:00Z, and an import refuses an earlier one with import-malformed.

The API token of a server on a data directory has to have at least 32 characters, and a server with a shorter one refuses to start. A server with a temporary data directory takes a shorter token.

Added in 1.3Rate limiting caps how many requests a client may send, by default 1,000 read-equivalent requests per second for every CPU, where a write counts as several. A global limit of 10,000 per CPU applies to all clients together, once to the requests with a valid API token and once to all others, and a client may hold at most 256 open streams, unless the limit per client is switched off.

The headers of a request have to arrive within 10 seconds, as does the TLS handshake over HTTPS, and a connection that stays idle for 120 seconds between two requests is closed. Keep the idle timeout of a reverse proxy or a pool of connections below that, so that it does not send a request over a connection that EventSourcingDB has just closed.

The headers of a request may take at most 1 MiB together. EventSourcingDB answers a request with larger headers with 431 Request Header Fields Too Large, before any route sees it.

Best Practices#

  • Monitor available disk space regularly.
  • Plan license upgrades in advance if approaching license-based event limits.
  • Implement chunked reading for large event queries to avoid transport overhead.

Understanding system limits helps ensure smooth operation and scalability as your event store grows.