Skip to content
Merged
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: 2 additions & 0 deletions examples/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
import restate

from greeter import greeter
from random_greeter import random_greeter
from virtual_object import counter
from workflow import payment
from pydantic_greeter import pydantic_greeter
from concurrent_greeter import concurrent_greeter

app = restate.app(services=[greeter,
random_greeter,
counter,
payment,
pydantic_greeter,
Expand Down
39 changes: 39 additions & 0 deletions examples/random_greeter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
# Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
#
# This file is part of the Restate SDK for Python,
# which is released under the MIT license.
#
# You can find a copy of the license in file LICENSE in the root
# directory of this repository or package, or at
# https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
#
"""example.py"""
# pylint: disable=C0116
# pylint: disable=W0613

from restate import Service, Context

random_greeter = Service("random_greeter")

@random_greeter.handler()
async def greet(ctx: Context, name: str) -> str:

# ctx.random() returns a Python Random instance seeded deterministically.
# By using ctx.random() you don't write entries in the journal,
# but you still get the same generated values on retries.

# To generate random numbers
random_number = ctx.random().randint(0, 100)

# To generate random bytes
random_bytes = ctx.random().randbytes(10)

# Use ctx.uuid() to generate a UUID v4 seeded deterministically
# As with ctx.random(), this won't write entries in the journal
random_uuid = ctx.uuid()

return (f"Hello {name} with "
f"random number {random_number}, "
f"random bytes {random_bytes!r} "
f"and uuid {random_uuid}!")
17 changes: 17 additions & 0 deletions python/restate/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"""

import abc
from random import Random
from uuid import UUID
from dataclasses import dataclass
from typing import Any, Awaitable, Callable, Dict, List, Optional, TypeVar, Union, Coroutine, overload, ParamSpec
import typing
Expand Down Expand Up @@ -219,6 +221,21 @@ def request(self) -> Request:
Returns the request object.
"""

@abc.abstractmethod
def random(self) -> Random:
"""
Returns a Random instance inherently predictable, deterministically seeded by Restate.

This instance is useful to generate identifiers, idempotency keys, and for uniform sampling from a set of options.
"""

@abc.abstractmethod
def uuid(self) -> UUID:
"""
Returns a random UUID, deterministically seeded.

This UUID will be stable across retries and replays.
"""

@typing_extensions.deprecated("`run` is deprecated, use `run_typed` instead for better type safety")
@overload
Expand Down
9 changes: 9 additions & 0 deletions python/restate/server_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

import asyncio
import contextvars
from random import Random
from datetime import timedelta
import inspect
import functools
from typing import Any, Awaitable, Callable, Dict, List, Optional, TypeVar, Union, Coroutine
import typing
import traceback
from uuid import UUID

from restate.context import DurablePromise, AttemptFinishedEvent, HandlerType, ObjectContext, Request, RestateDurableCallFuture, RestateDurableFuture, RunAction, SendHandle, RestateDurableSleepFuture, RunOptions, P
from restate.exceptions import TerminalError
Expand Down Expand Up @@ -278,6 +280,7 @@ def __init__(self,
self.invocation = invocation
self.attempt_headers = attempt_headers
self.send = send
self.random_instance = Random(invocation.random_seed)
self.receive = receive
self.run_coros_to_execute: dict[int, Callable[[], Awaitable[None]]] = {}
self.request_finished_event = asyncio.Event()
Expand Down Expand Up @@ -471,6 +474,12 @@ def request(self) -> Request:
attempt_finished_event=ServerTeardownEvent(self.request_finished_event),
)

def random(self) -> Random:
return self.random_instance

def uuid(self) -> UUID:
return UUID(int=self.random_instance.getrandbits(128), version=4)

# pylint: disable=R0914
async def create_run_coroutine(self,
handle: int,
Expand Down