I have the following code, and I want to make sure that the actions on the table are thread-safe (and make the accessor functions isolated). How should I go about it?
final table<Patient> key(id) patientTable = table [
{id: "a0384c7b-d22b-4627-9a2d-dfce02987c96", firstName: "John", lastName: "Doe", dob: {year: 1954, month: 10, day: 23}, gender: "male"},
{id: "722d6531-a9f3-4eb7-8735-aa7151f9bb39", firstName: "Jane", lastName: "Doe", dob: {year: 1972, month: 9, day: 17}, gender: "female"}
];
service / on EP {
isolated resource function post patients(@http:Payload PatientEntry patientEntry) returns Patient {
Patient patient = {id: uuid:createRandomUuid(), ...patientEntry};
patientTable.put(patient);
return patient;
}
isolated resource function get patients/[string patientId]() returns Patient|http:NotFound {
if !patientTable.hasKey(patientId) {
return {body: "Patient not found"};
}
return patientTable.get(patientId);
}
}
I can’t make the patientTable readonly, since I need to edit the table from the resource functions. Also tried having locks in all places where I access the patientTable, I still get invalid access of mutable storage in an 'isolated' function(BCE3943)
In addition to using locks you also need to make sure mutable values added to the table are isolated roots (any mutable state accessible via an isolated root is only accessible via the isolated root, ensuring exclusive access via the isolated root). Therefore, you’d have to use isolated expressions (e.g., patientEntry.clone()
) when transferring values in/out of the lock statements that access patientTable
. If these conditions are met, and patientTable
or anything dependent on the table aren’t public constructs, the compiler will infer the variable to be an isolated variable, which you can access within isolated
functions/methods.
You can also explicitly mark patientTable
as an isolated
variable. This will introduce additional compile-time validations including enforcing lock statements when accessing the table and transfer in/out validation.
isolated final table<Patient> key(id) patientTable = table [
...
];
import ballerina/uuid;
import ballerina/http;
type Patient record {readonly string id;};
type PatientEntry record {||};
final isolated table<Patient> key(id) patientTable = table [
{id: "a0384c7b-d22b-4627-9a2d-dfce02987c96", firstName: "John", lastName: "Doe", dob: {year: 1954, month: 10, day: 23}, gender: "male"},
{id: "722d6531-a9f3-4eb7-8735-aa7151f9bb39", firstName: "Jane", lastName: "Doe", dob: {year: 1972, month: 9, day: 17}, gender: "female"}
];
isolated service / on new http:Listener(8080) {
isolated resource function post patients(@http:Payload PatientEntry patientEntry) returns Patient {
Patient patient = {id: uuid:createRandomUuid(), ...patientEntry};
lock {
patientTable.put(patient.clone());
}
return patient;
}
isolated resource function get patients/[string patientId]() returns Patient|http:NotFound {
lock {
if !patientTable.hasKey(patientId) {
return {body: "Patient not found"};
}
return patientTable.get(patientId).clone();
}
}
}