Skip to content

Commit decad9f

Browse files
Remove unstable index from view readsets
1 parent 93105ea commit decad9f

6 files changed

Lines changed: 117 additions & 56 deletions

File tree

crates/core/src/host/module_host.rs

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,66 @@ pub struct CallViewParams {
10891089
pub timestamp: Timestamp,
10901090
}
10911091

1092+
pub(crate) struct ResolvedViewForRefresh<'a> {
1093+
pub view_id: ViewId,
1094+
pub table_id: TableId,
1095+
pub view_def: &'a ViewDef,
1096+
}
1097+
1098+
/// Lookup a module's [`ViewDef`] and check for consistency among
1099+
/// its readset, `st_view`, and the [`ModuleDef`].
1100+
pub(crate) fn resolve_view_for_refresh<'a>(
1101+
tx: &MutTxId,
1102+
module_def: &'a ModuleDef,
1103+
view_call: &ViewCallInfo,
1104+
) -> anyhow::Result<ResolvedViewForRefresh<'a>> {
1105+
let st_view = tx
1106+
.lookup_st_view(view_call.view_id)
1107+
.with_context(|| format!("failed to look up view {}", view_call.view_id))?;
1108+
1109+
let view_id = st_view.view_id;
1110+
let table_id = st_view
1111+
.table_id
1112+
.ok_or_else(|| anyhow::anyhow!("view {:?} does not have a backing table", view_id))?;
1113+
1114+
let view_name: Identifier = st_view.view_name.into();
1115+
let view_def = module_def.view(&view_name).ok_or_else(|| {
1116+
anyhow::anyhow!(
1117+
"view `{}` for view id `{}` not found in current module",
1118+
view_name,
1119+
view_id
1120+
)
1121+
})?;
1122+
1123+
let is_anonymous = view_call.sender.is_none();
1124+
1125+
if st_view.is_anonymous != is_anonymous {
1126+
return Err(anyhow::anyhow!(
1127+
"found is_anonymous={} in st_view, but {} in readset when updating view `{}`",
1128+
st_view.is_anonymous,
1129+
is_anonymous,
1130+
view_name,
1131+
));
1132+
}
1133+
1134+
let is_anonymous = view_def.is_anonymous;
1135+
1136+
if st_view.is_anonymous != is_anonymous {
1137+
return Err(anyhow::anyhow!(
1138+
"found is_anonymous={} in st_view, but {} in module when updating view `{}`",
1139+
st_view.is_anonymous,
1140+
is_anonymous,
1141+
view_name,
1142+
));
1143+
}
1144+
1145+
Ok(ResolvedViewForRefresh {
1146+
view_id,
1147+
table_id,
1148+
view_def,
1149+
})
1150+
}
1151+
10921152
pub struct CallProcedureParams {
10931153
pub timestamp: Timestamp,
10941154
pub caller_identity: Identity,
@@ -2897,17 +2957,21 @@ impl ModuleHost {
28972957
let mut total_duration = Duration::ZERO;
28982958
let mut abi_duration = Duration::ZERO;
28992959
let mut trapped = false;
2900-
for ViewCallInfo {
2901-
view_id,
2902-
table_id,
2903-
fn_ptr,
2904-
sender,
2905-
} in tx.views_for_refresh().cloned().collect::<Vec<_>>()
2906-
{
2907-
let Some(view_def) = module_def.get_view_by_id(fn_ptr, sender.is_none()) else {
2908-
outcome = ViewOutcome::Failed(format!("view with fn_ptr `{fn_ptr}` not found"));
2909-
break;
2960+
for view_call in tx.views_for_refresh().cloned().collect::<Vec<_>>() {
2961+
let sender = view_call.sender;
2962+
let resolved = match resolve_view_for_refresh(&tx, module_def, &view_call) {
2963+
Ok(resolved) => resolved,
2964+
Err(err) => {
2965+
outcome = ViewOutcome::Failed(format!("failed to resolve view: {err}"));
2966+
break;
2967+
}
29102968
};
2969+
let ResolvedViewForRefresh {
2970+
view_id,
2971+
table_id,
2972+
view_def,
2973+
} = resolved;
2974+
let view_name = &view_def.name;
29112975
let args = match FunctionArgs::Nullary.into_tuple_for_def(module_def, view_def) {
29122976
Ok(args) => args,
29132977
Err(err) => {
@@ -2919,7 +2983,7 @@ impl ModuleHost {
29192983
let (result, trap) = Self::call_view_inner(
29202984
instance,
29212985
tx,
2922-
&view_def.name,
2986+
view_name,
29232987
view_id,
29242988
table_id,
29252989
view_def.fn_ptr,

crates/core/src/host/v8/syscall/common.rs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use anyhow::Context;
2525
use bytes::Bytes;
2626
use spacetimedb_datastore::locking_tx_datastore::{FuncCallType, MutTxId, ViewCallInfo};
2727
use spacetimedb_lib::{ConnectionId, Identity, RawModuleDef, Timestamp};
28-
use spacetimedb_primitives::{ColId, IndexId, ProcedureId, TableId};
28+
use spacetimedb_primitives::{ColId, IndexId, ProcedureId, TableId, ViewFnPtr};
2929
use spacetimedb_sats::bsatn;
3030
use spacetimedb_schema::def::ModuleDef;
3131
use spacetimedb_schema::identifier::Identifier;
@@ -752,19 +752,22 @@ fn refresh_views(
752752

753753
for view_call in views_for_refresh {
754754
let res: SysCallResult<()> = (|| {
755-
let view_def = module_def
756-
.get_view_by_id(view_call.fn_ptr, view_call.sender.is_none())
757-
.ok_or_else(|| {
758-
TypeError(format!(
759-
"view with fn_ptr `{}` not found while refreshing procedure transaction",
760-
view_call.fn_ptr
761-
))
762-
.throw(scope)
763-
})?;
755+
let resolved = crate::host::module_host::resolve_view_for_refresh(
756+
tx.as_ref().expect("procedure tx missing during view refresh"),
757+
module_def,
758+
&view_call,
759+
)
760+
.map_err(|err| TypeError(format!("view refresh failed after procedure call: {err}")).throw(scope))?;
761+
762+
let table_id = resolved.table_id;
763+
let view_def = resolved.view_def;
764+
let view_name = &view_def.name;
765+
let fn_ptr = view_def.fn_ptr;
764766

765767
let current_tx = tx.take().expect("procedure tx missing during view refresh");
766-
let (next_tx, call_result) =
767-
tx_slot.set(current_tx, || call_view(scope, hooks, &view_call, &view_def.name));
768+
let (next_tx, call_result) = tx_slot.set(current_tx, || {
769+
call_view(scope, hooks, &view_call, &view_name, table_id, fn_ptr)
770+
});
768771
tx = Some(next_tx);
769772
let return_data = call_result?;
770773

@@ -819,7 +822,7 @@ fn refresh_views(
819822
.materialize_view(
820823
tx.as_mut()
821824
.expect("procedure tx missing while materializing authenticated view"),
822-
view_call.table_id,
825+
table_id,
823826
sender,
824827
rows,
825828
)
@@ -828,7 +831,7 @@ fn refresh_views(
828831
.materialize_anonymous_view(
829832
tx.as_mut()
830833
.expect("procedure tx missing while materializing anonymous view"),
831-
view_call.table_id,
834+
table_id,
832835
rows,
833836
)
834837
.map_err(NodesError::from)?,
@@ -857,6 +860,8 @@ fn call_view(
857860
hooks: &HookFunctions<'_>,
858861
view_call: &ViewCallInfo,
859862
view_name: &Identifier,
863+
table_id: TableId,
864+
fn_ptr: ViewFnPtr,
860865
) -> SysCallResult<ViewReturnData> {
861866
let prev_func_type = get_env(scope)?
862867
.instance_env
@@ -871,8 +876,8 @@ fn call_view(
871876
ViewOp {
872877
name: view_name,
873878
view_id: view_call.view_id,
874-
table_id: view_call.table_id,
875-
fn_ptr: view_call.fn_ptr,
879+
table_id,
880+
fn_ptr,
876881
args: &args,
877882
sender: &sender,
878883
timestamp: Timestamp::now(),
@@ -884,8 +889,8 @@ fn call_view(
884889
AnonymousViewOp {
885890
name: view_name,
886891
view_id: view_call.view_id,
887-
table_id: view_call.table_id,
888-
fn_ptr: view_call.fn_ptr,
892+
table_id,
893+
fn_ptr,
889894
args: &args,
890895
timestamp: Timestamp::now(),
891896
},

crates/core/src/host/wasm_common/module_host_actor.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,17 +1371,7 @@ impl InstanceCommon {
13711371
ViewResult::Rows(bytes) => deserialize_view_rows(row_type, bytes, typespace)
13721372
.context("Error deserializing rows returned by view".to_string())?,
13731373
ViewResult::RawSql(query) => self
1374-
.run_query_for_view(
1375-
&mut tx,
1376-
&query,
1377-
&row_product_type,
1378-
&ViewCallInfo {
1379-
view_id,
1380-
table_id,
1381-
fn_ptr,
1382-
sender,
1383-
},
1384-
)
1374+
.run_query_for_view(&mut tx, &query, &row_product_type, &ViewCallInfo { view_id, sender })
13851375
.context("Error executing raw SQL returned by view".to_string())?,
13861376
};
13871377

@@ -1798,8 +1788,6 @@ impl InstanceOp for ViewOp<'_> {
17981788
fn call_type(&self) -> FuncCallType {
17991789
FuncCallType::View(ViewCallInfo {
18001790
view_id: self.view_id,
1801-
table_id: self.table_id,
1802-
fn_ptr: self.fn_ptr,
18031791
sender: Some(*self.sender),
18041792
})
18051793
}
@@ -1828,8 +1816,6 @@ impl InstanceOp for AnonymousViewOp<'_> {
18281816
fn call_type(&self) -> FuncCallType {
18291817
FuncCallType::View(ViewCallInfo {
18301818
view_id: self.view_id,
1831-
table_id: self.table_id,
1832-
fn_ptr: self.fn_ptr,
18331819
sender: None,
18341820
})
18351821
}

crates/core/src/host/wasmtime/wasm_instance_env.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use spacetimedb_data_structures::map::IntMap;
1919
use spacetimedb_datastore::locking_tx_datastore::{FuncCallType, MutTxId, ViewCallInfo};
2020
use spacetimedb_lib::{bsatn, ConnectionId, Timestamp};
2121
use spacetimedb_primitives::errno::HOST_CALL_FAILURE;
22-
use spacetimedb_primitives::{errno, ColId};
22+
use spacetimedb_primitives::{errno, ColId, ViewFnPtr};
2323
use spacetimedb_schema::def::ModuleDef;
2424
use spacetimedb_schema::identifier::Identifier;
2525
use std::future::Future;
@@ -1697,13 +1697,20 @@ impl WasmInstanceEnv {
16971697

16981698
for view_call in views_for_refresh {
16991699
let res: anyhow::Result<()> = (|| {
1700-
let view_def = module_def
1701-
.get_view_by_id(view_call.fn_ptr, view_call.sender.is_none())
1702-
.ok_or_else(|| anyhow!("view with fn_ptr `{}` not found", view_call.fn_ptr))?;
1700+
let resolved = crate::host::module_host::resolve_view_for_refresh(
1701+
tx.as_ref().expect("procedure tx missing during view refresh"),
1702+
&module_def,
1703+
&view_call,
1704+
)?;
1705+
1706+
let table_id = resolved.table_id;
1707+
let view_def = resolved.view_def;
1708+
let view_name = &view_def.name;
1709+
let fn_ptr = view_def.fn_ptr;
17031710

17041711
let current_tx = tx.take().expect("procedure tx missing during view refresh");
17051712
let (next_tx, call_result) =
1706-
tx_slot.set(current_tx, || Self::call_view(caller, &view_call, &view_def.name));
1713+
tx_slot.set(current_tx, || Self::call_view(caller, &view_call, &view_name, fn_ptr));
17071714
tx = Some(next_tx);
17081715
let return_data = call_result?;
17091716

@@ -1731,14 +1738,14 @@ impl WasmInstanceEnv {
17311738
Some(sender) => stdb.materialize_view(
17321739
tx.as_mut()
17331740
.expect("procedure tx missing while materializing authenticated view"),
1734-
view_call.table_id,
1741+
table_id,
17351742
sender,
17361743
rows,
17371744
)?,
17381745
None => stdb.materialize_anonymous_view(
17391746
tx.as_mut()
17401747
.expect("procedure tx missing while materializing anonymous view"),
1741-
view_call.table_id,
1748+
table_id,
17421749
rows,
17431750
)?,
17441751
}
@@ -1766,6 +1773,7 @@ impl WasmInstanceEnv {
17661773
caller: &mut Caller<'a, Self>,
17671774
view_call: &ViewCallInfo,
17681775
view_name: &Identifier,
1776+
fn_ptr: ViewFnPtr,
17691777
) -> anyhow::Result<ViewReturnData> {
17701778
let prev_func_type = caller
17711779
.data_mut()
@@ -1792,7 +1800,7 @@ impl WasmInstanceEnv {
17921800
call_view,
17931801
call_view_anon,
17941802
view_name,
1795-
view_call.fn_ptr.0,
1803+
fn_ptr.0,
17961804
view_call.sender,
17971805
args_source.0,
17981806
result_sink,

crates/datastore/src/locking_tx_datastore/mut_tx.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use spacetimedb_lib::{
4747
ConnectionId, Identity, Timestamp,
4848
};
4949
use spacetimedb_primitives::{
50-
col_list, ArgId, ColId, ColList, ColSet, ConstraintId, IndexId, ScheduleId, SequenceId, TableId, ViewFnPtr, ViewId,
50+
col_list, ArgId, ColId, ColList, ColSet, ConstraintId, IndexId, ScheduleId, SequenceId, TableId, ViewId,
5151
};
5252
use spacetimedb_sats::{
5353
bsatn::to_writer, memory_usage::MemoryUsage, raw_identifier::RawIdentifier, ser::Serialize, AlgebraicValue,
@@ -79,8 +79,6 @@ use std::{
7979
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
8080
pub struct ViewCallInfo {
8181
pub view_id: ViewId,
82-
pub table_id: TableId,
83-
pub fn_ptr: ViewFnPtr,
8482
pub sender: Option<Identity>,
8583
}
8684

crates/smoketests/modules/views-auto-migrate-stable-updated/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub fn bump_counter(ctx: &ReducerContext) {
3636
}
3737

3838
// This view is intentionally prefixed with `a_` to change the index of
39-
// `target_counter` in the module's internal view list.
39+
// `z_counter` in the module's internal view list.
4040
#[view(accessor = a_wrong_shape, public)]
4141
pub fn a_wrong_shape(ctx: &ViewContext) -> Option<Marker> {
4242
ctx.db.marker().id().find(&0)

0 commit comments

Comments
 (0)