///|
/// Creates an empty storage-side fencing guard.
pub fn FenceGuard::new() -> FenceGuard {
{ marks: [] }
}
///|
fn clone_marks(marks : Array[FenceMark]) -> Array[FenceMark] {
marks.map(fn(mark) { mark })
}
///|
/// Returns the highest accepted token for a resource, or zero if unseen.
pub fn FenceGuard::highest_token(self : FenceGuard, resource : String) -> Int {
for mark in self.marks {
if mark.resource == resource {
return mark.token
}
}
0
}
///|
/// Validates a storage operation against the highest observed fencing token.
pub fn FenceGuard::check(
self : FenceGuard,
write : FencedWrite,
) -> FenceDecision {
if write.resource == "" {
return {
next_guard: self,
accepted: false,
highest_token: 0,
message: "resource must not be empty",
}
}
if write.holder == "" {
return {
next_guard: self,
accepted: false,
highest_token: self.highest_token(write.resource),
message: "holder must not be empty",
}
}
if write.operation == "" {
return {
next_guard: self,
accepted: false,
highest_token: self.highest_token(write.resource),
message: "operation must not be empty",
}
}
if write.token <= 0 {
return {
next_guard: self,
accepted: false,
highest_token: self.highest_token(write.resource),
message: "fencing token must be positive",
}
}
let marks = clone_marks(self.marks)
for index, mark in marks {
if mark.resource == write.resource {
if write.token < mark.token {
return {
next_guard: self,
accepted: false,
highest_token: mark.token,
message: "stale fencing token rejected",
}
}
if write.token == mark.token && write.holder != mark.holder {
return {
next_guard: self,
accepted: false,
highest_token: mark.token,
message: "token is bound to a different holder",
}
}
if write.token > mark.token {
marks[index] = {
resource: write.resource,
token: write.token,
holder: write.holder,
}
}
return {
next_guard: { marks, },
accepted: true,
highest_token: write.token,
message: "fenced write accepted",
}
}
}
marks.push({
resource: write.resource,
token: write.token,
holder: write.holder,
})
{
next_guard: { marks, },
accepted: true,
highest_token: write.token,
message: "fenced write accepted",
}
}
///|
/// Builds a write request directly from an active lease.
pub fn Lease::fenced_write(
self : Lease,
operation : String,
now : Int,
) -> FencedWrite? {
if self.is_active(now) {
Some({
resource: self.resource,
holder: self.holder,
token: self.token,
operation,
})
} else {
None
}
}