Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7a63a06
RavenDB-27140 Improve README and cover download failures
poissoncorp Jul 28, 2026
44e889c
RavenDB-27140 Make embedded shutdown graceful and bounded
poissoncorp Jul 28, 2026
ba39344
RavenDB-27140 Use RavenDB certificate thumbprints for admin access
poissoncorp Jul 28, 2026
58cb023
RavenDB-27140 Close open document stores safely
poissoncorp Jul 28, 2026
ca84b4f
RavenDB-27140 Support external certificate loaders
poissoncorp Jul 28, 2026
3ad6996
RavenDB-27140 Preserve runtime matcher failures
poissoncorp Jul 28, 2026
3e5e3a3
RavenDB-27140 Handle existing databases by exception type
poissoncorp Jul 28, 2026
de2f615
RavenDB-27140 Make document store creation thread-safe
poissoncorp Jul 28, 2026
eb8f776
RavenDB-27140 Synchronize server lifecycle cleanup
poissoncorp Jul 28, 2026
6ac7a5e
RavenDB-27140 Add embedded server lifecycle controls
poissoncorp Jul 28, 2026
8508222
RavenDB-27140 Report embedded server process exits
poissoncorp Jul 28, 2026
766c44f
RavenDB-27140 Drain server output streams concurrently
poissoncorp Jul 29, 2026
5d021bf
RavenDB-27140 Distinguish server startup timeouts
poissoncorp Jul 29, 2026
9b83f77
RavenDB-27140 Integrate embedded logs with Python logging
poissoncorp Jul 29, 2026
80240ab
RavenDB-27140 Open the canonical RavenDB Studio URL
poissoncorp Jul 29, 2026
f6f7da9
RavenDB-27140 Detect the bundled server runtime automatically
poissoncorp Jul 29, 2026
83138cc
RavenDB-27140 Add embedded licensing and explicit EULA acceptance
poissoncorp Jul 29, 2026
2721383
RavenDB-27140 Support independent embedded server instances
poissoncorp Jul 29, 2026
37d427e
RavenDB-27140 Deprecate the misleading ServerOptions instance alias
poissoncorp Jul 29, 2026
1f30968
RavenDB-27140 Remove the parent process ID fallback
poissoncorp Jul 29, 2026
626d7b2
RavenDB-27140 Keep failed embedded server starts retryable
poissoncorp Jul 29, 2026
1d9fe70
RavenDB-27140 Harden external server archive extraction
poissoncorp Jul 29, 2026
6ae7168
RavenDB-27140 Document and expose reusable server providers
poissoncorp Jul 29, 2026
48e617d
RavenDB-27140 Document the synchronous embedded execution model
poissoncorp Jul 29, 2026
a0acef4
RavenDB-27140 Test the bundled server in its installed location
poissoncorp Jul 29, 2026
3c3dc40
RavenDB-27140 Move default server data out of site-packages
poissoncorp Jul 29, 2026
805cd80
RavenDB-27140 Validate secured embedded client credentials early
poissoncorp Jul 29, 2026
220fcfe
RavenDB-27140 Fix the RavenDB EULA documentation link
poissoncorp Jul 29, 2026
1467aee
RavenDB-27140 Clarify customer-facing server behavior
poissoncorp Jul 29, 2026
985eb5f
RavenDB-27140 Add lifecycle and multi-instance examples
poissoncorp Jul 29, 2026
d01cc65
RavenDB-27140 Format Python sources
poissoncorp Jul 29, 2026
3fe4db0
RavenDB-27140 Add certificate key identifiers
poissoncorp Jul 29, 2026
b8f140d
RavenDB-27140 Preserve configuration defaults
poissoncorp Jul 29, 2026
9c495fe
RavenDB-27140 Preserve server-only secured setup
poissoncorp Jul 29, 2026
4458b58
RavenDB-27140 Prepare 7.2.5.post2 release
poissoncorp Jul 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
344 changes: 257 additions & 87 deletions README.md

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions labs/01-embedded-zero-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ The complete, runnable example is [`01_embedded_zero_config.py`](01_embedded_zer
The core is just:

```python
from ravendb_embedded import EmbeddedServer
from ravendb_embedded import EmbeddedServer, ServerOptions

options = ServerOptions()

with EmbeddedServer() as server:
server.start_server()
server.start_server(options)
with server.get_document_store("Lab") as store:
with store.open_session() as session:
session.store({"name": "Ayende"}, "people/1")
Expand All @@ -33,10 +35,10 @@ with EmbeddedServer() as server:
server (`Raven.Server.dll`) that ships inside the wheel.
2. `RavenServerRunner` launches it as **`dotnet Raven.Server.dll ...`**, using the `dotnet`
found on your `PATH` (`ServerOptions.dot_net_path`).
3. `ServerOptions.framework_version` is empty by default, so no `--fx-version` is passed and
standard .NET host resolution applies. The host does **not** roll forward across major
versions, which is why a net10.0 server hard-fails on a machine that only has .NET 8
(error: "You must install ... version 10.0.x").
3. `ServerOptions.framework_version` defaults to `""`, leaving runtime selection and roll-forward
to the dotnet host. Set it to `auto` when you want the package to read
`Raven.Server.runtimeconfig.json` and select a compatible installed patch. A net10.0 server
still requires .NET 10.
4. The one exception: point the server at a **self-contained** build (an apphost
`Raven.Server` with the runtime bundled) and it runs **without `dotnet`**. That is Lab 02.

Expand Down
11 changes: 7 additions & 4 deletions labs/04-embedded-secured.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ with EmbeddedServer() as server:

## Certificates

`secured()` needs a server `.pfx` and a client `.pem`; a password and a CA certificate are
optional. In production you bring your own (for example from RavenDB's setup wizard or your CA).
The lab generates a throwaway self-signed pair so it can run unattended; that generator is demo
code, not something to ship. A RavenDB server certificate needs the right extensions
The server `.pfx` is required. The client `.pem` is needed when `EmbeddedServer` creates
authenticated document stores; a password and a CA certificate are optional. To configure only
the HTTPS server and use clients managed elsewhere, call `options.secured(server_pfx_path)`.

In production you bring your own certificates (for example from RavenDB's setup wizard or your
CA). The lab generates a throwaway self-signed pair so it can run unattended; that generator is
demo code, not something to ship. A RavenDB server certificate needs the right extensions
(digitalSignature key usage, serverAuth/clientAuth EKU, and SANs for how the server is reached),
which the lab's generator sets.

Expand Down
2 changes: 2 additions & 0 deletions labs/04_embedded_secured.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ def _demo_certificates(directory):
.add_extension(
x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH, ExtendedKeyUsageOID.CLIENT_AUTH]), critical=False
)
.add_extension(x509.SubjectKeyIdentifier.from_public_key(key.public_key()), critical=False)
.add_extension(x509.AuthorityKeyIdentifier.from_issuer_public_key(key.public_key()), critical=False)
.sign(key, hashes.SHA256())
)
cert_pem = cert.public_bytes(serialization.Encoding.PEM)
Expand Down
5 changes: 3 additions & 2 deletions labs/05-embedded-persistent.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ with EmbeddedServer() as server:

## Notes

- The default `data_directory` is a folder under the package location, which is fine for tests
but not what you want for real data. Set it explicitly to a path you control.
- The default `data_directory` remains `RavenDB` inside the installed package directory. Set it
explicitly when the package directory may be read-only or the application needs a fixed,
application-owned location.
- `get_document_store("Lab")` reuses the existing database on the second run; it only creates one
when it is missing.

Expand Down
56 changes: 56 additions & 0 deletions labs/06-embedded-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Lab 06: Server lifecycle and process monitoring

**For:** applications that need to stop or restart their managed RavenDB process and observe
whether a process exit was expected or unexpected.

## Run it

```bash
pip install ravendb-embedded
python labs/06_embedded_lifecycle.py
```

The complete, self-checking example is
[`06_embedded_lifecycle.py`](06_embedded_lifecycle.py). It exercises:

- `get_server_process_id()`
- `stop_server()`
- `restart_server()`
- `add_server_process_exited()`
- document access after restart

The core lifecycle flow is:

```python
from threading import Event

from ravendb_embedded import EmbeddedServer, ServerOptions

options = ServerOptions()

exit_observed = Event()
exit_events = []

with EmbeddedServer() as server:
server.add_server_process_exited(
lambda event: (exit_events.append(event), exit_observed.set())
)
server.start_server(options)
first_process_id = server.get_server_process_id()

server.stop_server()
assert exit_observed.wait(10)
assert exit_events[-1].process_id == first_process_id
assert exit_events[-1].expected

server.restart_server()
assert server.get_server_process_id() != first_process_id
```

`event.expected` is `True` for `stop_server()`, `restart_server()`, and context-manager cleanup.
It is `False` when RavenDB exits on its own.

## Takeaway

`EmbeddedServer` owns the RavenDB child process but exposes enough lifecycle information for an
application to coordinate restarts and distinguish planned shutdowns from server failures.
53 changes: 53 additions & 0 deletions labs/06_embedded_lifecycle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Lab 06: Stop, restart, process IDs, and process-exit notifications."""

import tempfile
from pathlib import Path
from threading import Event

from ravendb_embedded import EmbeddedServer, ServerOptions


def main() -> None:
with tempfile.TemporaryDirectory() as work:
options = ServerOptions()
options.data_directory = str(Path(work, "data"))
options.logs_path = str(Path(work, "logs"))

exit_observed = Event()
exit_events = []

def on_server_exit(event) -> None:
exit_events.append(event)
exit_observed.set()

with EmbeddedServer() as server:
server.add_server_process_exited(on_server_exit)
server.start_server(options)
first_process_id = server.get_server_process_id()
print(f"Started RavenDB process {first_process_id}")

server.stop_server()
if not exit_observed.wait(10):
raise RuntimeError("The server exit callback was not invoked.")

stopped = exit_events[-1]
assert stopped.process_id == first_process_id
assert stopped.expected
print(f"Stopped RavenDB process {stopped.process_id}")

exit_observed.clear()
server.restart_server()
second_process_id = server.get_server_process_id()
assert second_process_id != first_process_id
print(f"Restarted RavenDB as process {second_process_id}")

with server.get_document_store("LifecycleLab") as store:
with store.open_session() as session:
session.store({"status": "running"}, "status/1")
session.save_changes()

print("PASS: stop, exit notification, restart, and document access succeeded.")


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions labs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ the released library and server binaries used by the scripts.
| [03](03-on-demand-server.md) | On-demand cached self-contained download (no manual steps) | No |
| [04](04-embedded-secured.md) | Secured embedded server (HTTPS + client certificate) | Yes |
| [05](05-embedded-persistent.md) | Persistent data directory (data survives restarts) | Yes |
| [06](06-embedded-lifecycle.md) | Stop, restart, PID, and process-exit monitoring | Yes |

RavenDB version to .NET mapping: **7.1.x needs .NET 8, 7.2.x needs .NET 10.** The bundled server
decides this, so it can change on a minor bump; check the lab for your version.
Expand Down
11 changes: 9 additions & 2 deletions ravendb_embedded/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
from ravendb_embedded.embedded_server import EmbeddedServer
from ravendb_embedded.options import DatabaseOptions, ServerOptions, SecurityOptions
from ravendb_embedded.embedded_server import (
EmbeddedServer,
ServerProcessExitedEvent,
ServerStartupError,
ServerStartupTimeoutError,
)
from ravendb_embedded.options import DatabaseOptions, LicensingOptions, ServerOptions, SecurityOptions
from ravendb_embedded.on_demand import ensure_server
from ravendb_embedded.provide import (
CopyServerFromNugetProvider,
CopyServerProvider,
ExternalServerProvider,
ExtractFromPkgResourceServerProvider,
ExtractFromZipServerProvider,
ProvideRavenDBServer,
)
Loading
Loading