pub trait Object:
Any
+ Clone
+ Eq
+ Send
+ Sync
+ DeepSizeOf
+ for<'a> Deserialize<'a>
+ Serialize {
type Event: Event;
// Required methods
fn type_ulid() -> &'static TypeId;
fn can_create<'a, C>(
&'a self,
user: User,
self_id: ObjectId,
db: &'a C,
) -> impl Future + 'a
where C: CanDoCallbacks;
fn can_apply<'a, C>(
&'a self,
user: User,
self_id: ObjectId,
event: &'a Self::Event,
db: &'a C,
) -> impl Future + 'a
where C: CanDoCallbacks;
fn users_who_can_read<'a, C>(&'a self, db: &'a C) -> impl Future + 'a
where C: CanDoCallbacks;
fn apply(&mut self, self_id: DbPtr<Self>, event: &Self::Event);
fn required_binaries(&self) -> Vec<BinPtr>;
// Provided methods
fn snapshot_version() -> i32 { ... }
fn from_old_snapshot(version: i32, data: Value) -> Result<Self, Error> { ... }
}
Expand description
Note that for this trait to be implemented correctly, the Eq
trait should be
equivalent to equality of the JSON-serialized representation. In particular, this
means that things like [HashMap
]s should be banned, and [BTreeMap
]s should be
preferred.
Note that due to postgresql limitations reasons, this type MUST NOT include any null byte in the serialized JSON. Including them will result in internal server errors.
Required Associated Types§
Required Methods§
fn type_ulid() -> &'static TypeId
fn can_create<'a, C>(
&'a self,
user: User,
self_id: ObjectId,
db: &'a C,
) -> impl Future + 'awhere
C: CanDoCallbacks,
Sourcefn can_apply<'a, C>(
&'a self,
user: User,
self_id: ObjectId,
event: &'a Self::Event,
db: &'a C,
) -> impl Future + 'awhere
C: CanDoCallbacks,
fn can_apply<'a, C>(
&'a self,
user: User,
self_id: ObjectId,
event: &'a Self::Event,
db: &'a C,
) -> impl Future + 'awhere
C: CanDoCallbacks,
Note that permissions are always checked with the latest version of the object on the server. So, due to this, CRDB objects are not strictly speaking a CRDT. However, it is required to do so for security, because otherwise a user who lost permissions would still be allowed to submit events antidated to before the permission loss, which would be bad as users could re-grant themselves permissions.
Sourcefn users_who_can_read<'a, C>(&'a self, db: &'a C) -> impl Future + 'awhere
C: CanDoCallbacks,
fn users_who_can_read<'a, C>(&'a self, db: &'a C) -> impl Future + 'awhere
C: CanDoCallbacks,
Note that db.get
calls will be cached. So:
-
Use
db.get
as little as possible, to avoid useless cache thrashing -
Make sure to always read objects in a given order. You should consider all your objects as forming a DAG, and each object’s
users_who_can_read
function should:- Only ever operate on a topological sort of the DAG
- Only call
db.get
on objects after this object on the topological sort
Failing to do this might lead to deadlocks within the database, which will result in internal server errors from postgresql. For example, if you have A -> B -> C and A -> C, A’s
users_who_can_read
should first callget
onB
before calling it onC
, because otherwise B could be running the same function onC
and causing a deadlock. Similarly, if A and B both depend on C and D, thenusers_who_can_read
for A and B should always lock C and D in the same order, to avoid deadlocks.
In other words, you should consider db.get()
as taking a lock on the obtained object: there
must exist a total order for which the vector, consisting of self
and then all the C::get
calls in-order, is sorted.
In particular, any recursive call of users_who_can_read
is most likely wrong.
fn apply(&mut self, self_id: DbPtr<Self>, event: &Self::Event)
fn required_binaries(&self) -> Vec<BinPtr>
Provided Methods§
fn snapshot_version() -> i32
Sourcefn from_old_snapshot(version: i32, data: Value) -> Result<Self, Error>
fn from_old_snapshot(version: i32, data: Value) -> Result<Self, Error>
Parse this object type from an older snapshot version
Note that all metadata, in particular required_binaries
and users_who_can_read
MUST NOT change with a change in versioning. This method is designed only for
changing the on-the-wire representation of an object, not for changing its semantics.
Semantics changes should happen by sending an “upgrade” event to the object, and if cleanup is warranted then performing mass object recreation on the server afterwise.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.