Skip to content

Decorators

define_component

define_component(
    _cls=None,
    /,
    *,
    namespace: str = 'default',
    force: bool = False,
    permission=<Permission.USER: 2>,
    volatile=False,
    backend: str = 'default',
    rls_compare: tuple[str, str, str] | None = None,
) -> Callable[[type[hetu.data.component.BaseComponent]], type[hetu.data.component.BaseComponent]] | type[hetu.data.component.BaseComponent]

Source: hetu/data/component.py:371

定义Component组件的schema模型

Parameters

  • namespace (Any) — 你的项目名,主要为了区分不同项目的同名Component。 不同于System,Component的Namespace可以随意填写,只要被System引用了都会加载。 如果为"core",则此Component即使没被任何System引用,也会被加载。

  • volatile (Any) — 是否是易失表,设为True时,每次维护你的数据会被清除,请小心。

  • backend (Any) — 指定Component后端,对应配置文件中BACKENDS的字典key。默认为default,对应BACKENDS配置中第一个

  • permission (Any) — 设置读取权限,只对hetu client sdk连接起作用,服务器端代码不受限制。

  • everybody: 任何客户端连接都可以读,适合读一些服务器状态类的数据,如在线人数

  • user: 只有已登录的客户端都连接可以读

  • admin: 只有管理员权限客户端连接可以读

  • owner: 只能读取到owner属性值==登录的用户id(ctx.caller)的行,未登录的客户端无法读取。 此权限等同rls权限,且rls_compare=('eq', 'owner', 'caller')

  • rls: 行级权限,需要配合rls_compare参数使用,定义具体的行级权限逻辑

  • rls_compare (Any) — 当permission设置为RLS(行级权限)时,定义行级安全的比较函数和属性名。

  • rls_compare[0]: operator比较方法字符串,如"lt", “gt"等。参考python operator标准运算符函数模块

  • rls_compare[1]: 组件属性名字符串

  • rls_compare[2]: Context属性名字符串,或Context.user_data的key名

只有operator比较后返回True时允许读取此行。如果属性不存在,按nan处理(无法和任何值比较)。

  • force (Any) — 强制覆盖同名Component,单元测试用。

  • _cls (Any) — 当所有参数使用默认值时,可以直接无参数使用,如::

    @define_component class Position(BaseComponent): …

Examples

>>> import hetu
>>> import numpy as np
>>> @hetu.define_component(namespace="ssw")
... class Position(hetu.BaseComponent):
...     x: np.float32 = hetu.property_field(default=0)
...     y: np.float32 = hetu.property_field(default=0)
...     owner: np.int64 = hetu.property_field(default=0, unique=True)
...     name: str = hetu.property_field(default="12345678", dtype="U8")

Notes

property_field(default, unique, index, dtype) 是Component的属性定义,可定义默认值和数据类型。 - index 表示此属性开启索引; - unique 表示属性值必须唯一,启动此项默认会同时打开index。

.. warning:: ⚠️ 警告:索引会降低全表性能,请控制数量。其中unique索引降低的更多。

属性值的类型由type hint决定(如 : np.float32),请使用长度明确的np类型。 字符串类型格式为”<U8",U是Unicode,8表示长度,<表示little-endian。 不想看到"<U8"在IDE里标红语法错误的话,可用 name: str = property_field(dtype='<U8') 方式。

每个Component表都有个默认的主键id: np.int64 = property_field(default=雪花uuid, unique=True), 是一个uuid,无法修改。


define_endpoint

define_endpoint(
    namespace: str = 'global',
    force: bool = False,
    permission: hetu.common.permission.Permission = <Permission.USER: 2>,
)

Source: hetu/endpoint/definer.py:92

把一个函数包装成可供客户端远程调用的接口。

大部分情况不需要调用此包装器,你可以把逻辑代码直接写在System中。 System在定义时,如果设置了permission,则会自动生成对应的Endpoint。 如果你需要执行多次System,或不牵涉数据库操作的逻辑,可以使用Endpoint。

Parameters

  • namespace (Any) — 是你的项目名,服务器启动时,一个网络地址只能绑定一个namespace下的Endpoint们。 定义为"global"的Endpoint永远会被绑定并启用,用于在项目间通用。

  • force (Any) — 遇到重复定义是否强制覆盖前一个, 单元测试用

  • permission (Any) — 设置客户端的调用权限,只做些初级检查,具体权限需要自己逻辑中判断。

  • everybody: 任何客户端连接都可以调用执行。(不安全)

  • user: 只有已登录客户端连接可以调用

  • owner: 不可用 OWNER权限这里不可使用,需要自行做安全检查

  • admin: 只有管理员权限客户端连接可以调用

  • rls: 不可用 RLS权限这里不可使用,需要自行做安全检查

Examples

>>> import hetu
>>>
>>> @hetu.define_endpoint(namespace="example", permission=hetu.Permission.EVERYBODY)
... async def pay(ctx: hetu.EndpointContext, order_id, paid):
...     await ctx.systems.call("SystemName", order_id, paid)
...     return hetu.ResponseToClient(['anything', 'blah blah'])

Notes

hetu.system.define_system : define_system装饰器定义System hetu.endpoint.Context : Context类定义


define_route

define_route(uri: str, **sanic_kwargs: Any)

Source: hetu/webext.py:44

把一个async函数注册为HTTP端点,挂载到HeTu的web服务器上。

用于文件下载、后台页面、健康检查等和HeTu数据无关的普通web请求。它们是纯粹的 Sanic路由,hetu只负责帮你挂上去:不经过客户端的权限体系(Permission ), 也不提供数据库/System访问,鉴权、限流等都需要你自己在函数内处理。

需要客户端SDK调用的游戏逻辑请用 define_system / define_endpoint ,不是这个。

Register an async function as a plain HTTP endpoint on HeTu’s web server, for things unrelated to HeTu’s data (file downloads, admin pages, health checks). These routes bypass HeTu’s Permission system entirely and get no database access, so any authentication is up to you.

Parameters

  • uri (Any) — 路由路径,必须以"/“开头,可使用Sanic的路径参数语法(如 /file/<name:str>)。 不能占用HeTu保留的 /hetu 前缀。注册”/“会覆盖HeTu的默认欢迎页。

  • **sanic_kwargs (Any) — 原样透传给Sanic的 Blueprint.route,常用的有 methods=["POST"]stream=Truewebsocket=True 等。

Examples

>>> import hetu
>>> from sanic import Request, response
>>>
>>> @hetu.define_route("/download/<name:str>")
... async def download(request: Request, name: str):
...     return await response.file(f"/data/{name}")

Notes

hetu.on_server_setup : 直接拿到Sanic app做任意配置(静态目录、中间件等)


define_system

define_system(
    components: tuple[type[BaseComponent], ...] | None = None,
    namespace: str = 'default',
    force: bool = False,
    permission: hetu.common.permission.Permission | None = None,
    retry: int = 9999,
    depends: tuple[str | function, ...] = (),
    call_lock=False,
    on_start=False,
)

Source: hetu/system/definer.py:341

定义System,System类似数据库的储存过程,主要用于数据CRUD。 如果permission不为None,则会自动创建一个Endpoint,让客户端可以直接调用System, 并自动判断permission是否符合。

如果需要更多的控制,可以自己写@endpoint()调用System。

Parameters

  • namespace (Any) — 是你的项目名,服务器启动时,一个网络地址只能绑定一个namespace下的System们。 定义为"global"的System永远会被绑定并启用,用于在项目间通用。

  • components (Any) — 引用Component,引用的Component可以在ctx中进行相关的事务操作,保证数据一致性。 所有引用的Components会加入共置簇(Colocation Cluster)中,指放在同一个物理数据库中, 具体见Notes。

  • force (Any) — 遇到重复定义是否强制覆盖前一个, 单元测试用

  • permission (Any) — 设置客户端的调用权限,只做些初级检查,具体权限需要自己逻辑中判断。 设为None时表示客户端SDK不可调用,设置任意权限会创建一个供客户端调用的Endpoint, 然后由此Endpoint调用本System。

  • everybody: 任何客户端连接都可以调用执行。(不安全)

  • user: 只有已登录客户端连接可以调用

  • owner: 不可用 OWNER权限这里不可使用,需要自行做安全检查

  • admin: 只有管理员权限客户端连接可以调用

  • rls: 不可用 RLS权限这里不可使用,需要自行做安全检查

  • retry (Any) — 如果System遇到事务冲突,会重复执行直到成功。设为0关闭

  • depends (Any) — 定义要事务依赖的其他System,调用时会在同一个事务会话中执行。 可传入System函数本身,或字符串,如(“system1”, system2)。 可通过ctx.depend["system1"](ctx, ...)或直接system2(ctx, ...)方式调用定义的函数。 如果希望使用System副本,可以使用字符串式定义,加’:副本名后缀’。具体见Notes。 注意: 所有depends,将被合并进同一个共置簇中。

  • call_lock (Any) — 是否对此System启用调用锁,启用后在调用时可以通过传入调用UUID来防止System重复执行。 如果此System需要给未来调用使用,则此项必须为True。

客户端直接调用的System不需要此功能,主要用于未来调用的幂等性, 或者你需要嵌套执行System,保证其中一个只执行一次等特殊情况,

  • on_start (Any) — 标记此System为"启动钩子”:每次hetu start启动、开始收连接前,引擎会对每个instance 执行一次。

“每次hetu start只执行一次"由SystemLock实现:同次开服的所有worker共享同一boot uuid,去重到只 成功提交一次(并发由乐观锁收敛);下次hetu start换新uuid,故会再次执行。会自动启用 call_lock(去重所需),无需手动设置。

注意:事务可能因竞态/多worker重试,System应自身幂等;外部I/O (写文件、调外部存储等)可能执行多次,需自行把握(可通过提前提交事务判断事务是否成功)。

启动System因为跑在收连接之前,卡住就等于服务器起不来,所以有超时保护:它和连后端、 建表共用配置文件的 STARTUP_TIMEOUT 总预算(默认60秒),超时会报出卡在哪个System 并中止开服(退出码非0)。需要跑更久的话调大该配置(0为不限制)。

Examples

>>> import hetu
>>> # 定义Component
>>> @hetu.define_component
... class Stock(hetu.BaseComponent):
...     owner: int = hetu.property_field(0)
...     value: int = hetu.property_field(0)
>>> @hetu.define_component
... class Order(hetu.BaseComponent):
...     owner: int = hetu.property_field(0)
...     paid: bool = hetu.property_field(False)
...     qty: int = hetu.property_field(0)
>>>
>>> # 定义System
>>> @hetu.define_system(namespace="example", components=(Stock, Order), permission=hetu.Permission.USER)
... async def pay(ctx: hetu.SystemContext, order_id, paid):
...     async with ctx.repo[Order].upsert(id=order_id) as order:
...        order.paid = paid
...     async with ctx.repo[Order].upsert(owner=order.owner) as stock:
...        stock.value += order.qty
...     # ctx.commit()  # 可以省略,也可以提前提交
...     return hetu.ResponseToClient(['anything', 'blah blah'])

Notes

hetu.system.SystemContext : SystemContext类定义 hetu.endpoint.endpoint : endpoint装饰器定义Endpoint


on_server_setup

on_server_setup(func: collections.abc.Callable) -> collections.abc.Callable

Source: hetu/webext.py:108

注册一个web服务器配置回调,在Sanic app创建后调用,参数为app本身。

define_route 覆盖不到的场景用它:静态文件目录、中间件、异常处理器、自建带 url_prefix 的Blueprint等,都可以在回调里直接对app操作。

Register a callback invoked with the Sanic app right after it is created, for anything define_route cannot express: static directories, middleware, error handlers, your own prefixed Blueprint, and so on.

Examples

>>> import hetu
>>> from sanic import Sanic
>>>
>>> @hetu.on_server_setup
... def setup(app: Sanic):
...     app.static("/download", "/data/files")

Notes

hetu.define_route : 直接注册单条HTTP路由的快捷方式


property_field

property_field(
    default: Any,
    unique: bool = False,
    index: bool | None = None,
    dtype: str | type = '',
) -> Any

Source: hetu/data/component.py:51

Define a Component field declaration helper.

定义 Component 的属性字段。它通常与 type hint 一起使用,在 @define_component 处理 class 时被解析为内部 Property 定义。

Parameters

  • default (Any) — 属性默认值。所有字段都必须提供默认值,且不能为 None。 HeTu 的 Component 使用 c-struct like 的定长数据模型,不支持 nullable。

  • unique (bool, default False) — 是否为唯一索引。开启后字段值必须唯一,并会自动启用 index

  • index (bool | None, default None) — 是否建立索引。

  • None 表示沿用 unique 的值;

  • False 表示不建立普通索引;

  • True 表示建立普通索引。

unique=True 时,即使显式传入 False,后续定义阶段也会被强制修正为 True

  • dtype (str | type, default "") — 字段数据类型。留空时默认使用属性的 type hint。

推荐使用长度明确的 NumPy dtype,例如 np.int64np.float32"U8""<U32"。字符串类型需要显式指定长度。

Returns

内部 Property 对象。返回类型标注为 Any 是为了减少类型检查器对 class attribute default value 的误报,运行时仍会被当作 Property 处理。

Examples

>>> import numpy as np
>>> class Position(BaseComponent):
...     x: np.float32 = property_field(default=0)
...     owner: np.int64 = property_field(default=0, unique=True)
...     name: str = property_field(default="hero", dtype="U16")

Notes

property_field(...) 只负责声明字段元数据,真正的合法性校验会在 @define_component 执行时完成,包括:

  • 字段名是否合法;
  • defaultdtype 是否兼容;
  • dtype 是否可用于 NumPy structured array;
  • unique/index 组合是否合法。