mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-11 02:00:04 -04:00
e4098f98d9
## Description of Changes
This PR primarily affects the `bindings-macro` and `schema` crates to
review:
### Core changes
1. Replaces the `name` macro with `accessor` for **Tables, Views,
Procedures, and Reducers** in Rust modules.
2. Extends `RawModuleDefV10` with a new section for:
* case conversion policies
* explicit names
New sections are not validated in this PR so not functional.
3. Updates index behavior:
* Index names are now always **system-generated** for clients. Which
will be fixed in follow-up PR when we start validating RawModuleDef with
explicit names.
* The `accessor` name for an index is used only inside the module.
## Breaking changes (API/ABI)
1. **Rust modules**
* The `name` macro must be replaced with `accessor`.
2. **Client bindings (all languages)**
* Index names are now system-generated instead of using explicitly
provided names.
**Complexity:** 3
A follow-up PR will reintroduce explicit names with support for case
conversion.
---------
Co-authored-by: rekhoff <r.ekhoff@clockworklabs.io>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <bot@clockworklabs.com>
175 lines
4.5 KiB
Rust
175 lines
4.5 KiB
Rust
//! STDB module used for benchmarks based on "realistic" workloads we are focusing in improving.
|
|
use crate::Load;
|
|
use spacetimedb::{log, ReducerContext, SpacetimeType, Table, Timestamp};
|
|
use std::hint::black_box;
|
|
|
|
#[derive(SpacetimeType, Debug, Clone, Copy)]
|
|
pub struct Vector2 {
|
|
pub x: f32,
|
|
pub y: f32,
|
|
}
|
|
|
|
// ---------- schemas ----------
|
|
|
|
#[spacetimedb::table(accessor = entity)]
|
|
pub struct Entity {
|
|
#[auto_inc]
|
|
#[primary_key]
|
|
pub id: u32,
|
|
pub position: Vector2,
|
|
pub mass: u32,
|
|
}
|
|
|
|
impl Entity {
|
|
pub fn new(id: u32, x: f32, y: f32, mass: u32) -> Self {
|
|
Self {
|
|
id,
|
|
mass,
|
|
position: Vector2 { x, y },
|
|
}
|
|
}
|
|
}
|
|
|
|
#[spacetimedb::table(accessor = circle)]
|
|
pub struct Circle {
|
|
#[primary_key]
|
|
pub entity_id: u32,
|
|
#[index(btree)]
|
|
pub player_id: u32,
|
|
pub direction: Vector2,
|
|
pub magnitude: f32,
|
|
pub last_split_time: Timestamp,
|
|
}
|
|
|
|
impl Circle {
|
|
pub fn new(entity_id: u32, player_id: u32, x: f32, y: f32, magnitude: f32, last_split_time: Timestamp) -> Self {
|
|
Self {
|
|
entity_id,
|
|
player_id,
|
|
direction: Vector2 { x, y },
|
|
magnitude,
|
|
last_split_time,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[spacetimedb::table(accessor = food)]
|
|
pub struct Food {
|
|
#[primary_key]
|
|
pub entity_id: u32,
|
|
}
|
|
|
|
impl Food {
|
|
pub fn new(entity_id: u32) -> Self {
|
|
Self { entity_id }
|
|
}
|
|
}
|
|
|
|
fn mass_to_radius(mass: u32) -> f32 {
|
|
(mass as f32).sqrt()
|
|
}
|
|
|
|
fn is_overlapping(entity1: &Entity, entity2: &Entity) -> bool {
|
|
let entity1_radius = mass_to_radius(entity1.mass);
|
|
let entity2_radius = mass_to_radius(entity2.mass);
|
|
let distance =
|
|
((entity1.position.x - entity2.position.x).powi(2) + (entity1.position.y - entity2.position.y).powi(2)).sqrt();
|
|
distance < entity1_radius.max(entity2_radius)
|
|
}
|
|
|
|
// ---------- insert bulk ----------
|
|
#[spacetimedb::reducer]
|
|
pub fn insert_bulk_entity(ctx: &ReducerContext, count: u32) {
|
|
for id in 0..count {
|
|
ctx.db
|
|
.entity()
|
|
.insert(Entity::new(0, id as f32, (id + 5) as f32, id * 5));
|
|
}
|
|
log::info!("INSERT ENTITY: {count}");
|
|
}
|
|
|
|
#[spacetimedb::reducer]
|
|
pub fn insert_bulk_circle(ctx: &ReducerContext, count: u32) {
|
|
for id in 0..count {
|
|
ctx.db.circle().insert(Circle::new(
|
|
id,
|
|
id,
|
|
id as f32,
|
|
(id + 5) as f32,
|
|
(id * 5) as f32,
|
|
ctx.timestamp,
|
|
));
|
|
}
|
|
log::info!("INSERT CIRCLE: {count}");
|
|
}
|
|
|
|
#[spacetimedb::reducer]
|
|
pub fn insert_bulk_food(ctx: &ReducerContext, count: u32) {
|
|
for id in 1..=count {
|
|
ctx.db.food().insert(Food::new(id));
|
|
}
|
|
log::info!("INSERT FOOD: {count}");
|
|
}
|
|
|
|
// Simulate
|
|
// ```
|
|
// SELECT * FROM Circle, Entity, Food
|
|
// ```
|
|
#[spacetimedb::reducer]
|
|
pub fn cross_join_all(ctx: &ReducerContext, expected: u32) {
|
|
let mut count = 0;
|
|
for _circle in ctx.db.circle().iter() {
|
|
for _entity in ctx.db.entity().iter() {
|
|
for _food in ctx.db.food().iter() {
|
|
count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
log::info!("CROSS JOIN ALL: {expected}, processed: {count}");
|
|
}
|
|
|
|
// Simulate
|
|
// ```
|
|
// SELECT * FROM Circle JOIN ENTITY USING(entity_id), Food JOIN ENTITY USING(entity_id)
|
|
// ```
|
|
#[spacetimedb::reducer]
|
|
pub fn cross_join_circle_food(ctx: &ReducerContext, expected: u32) {
|
|
let mut count = 0;
|
|
for circle in ctx.db.circle().iter() {
|
|
let Some(circle_entity) = ctx.db.entity().id().find(circle.entity_id) else {
|
|
continue;
|
|
};
|
|
|
|
for food in ctx.db.food().iter() {
|
|
count += 1;
|
|
let food_entity = ctx
|
|
.db
|
|
.entity()
|
|
.id()
|
|
.find(food.entity_id)
|
|
.unwrap_or_else(|| panic!("Entity not found: {}", food.entity_id));
|
|
black_box(is_overlapping(&circle_entity, &food_entity));
|
|
}
|
|
}
|
|
|
|
log::info!("CROSS JOIN CIRCLE FOOD: {expected}, processed: {count}");
|
|
}
|
|
|
|
#[spacetimedb::reducer]
|
|
pub fn init_game_circles(ctx: &ReducerContext, initial_load: u32) {
|
|
let load = Load::new(initial_load);
|
|
|
|
insert_bulk_food(ctx, load.initial_load);
|
|
insert_bulk_entity(ctx, load.initial_load);
|
|
insert_bulk_circle(ctx, load.small_table);
|
|
}
|
|
|
|
#[spacetimedb::reducer]
|
|
pub fn run_game_circles(ctx: &ReducerContext, initial_load: u32) {
|
|
let load = Load::new(initial_load);
|
|
|
|
cross_join_circle_food(ctx, initial_load * load.small_table);
|
|
cross_join_all(ctx, initial_load * initial_load * load.small_table);
|
|
}
|