haxetink / tink_sql

SQL embedded into Haxe
MIT License
54 stars 17 forks source link

Proposal: add optional cache for tables #156

Open serjek opened 3 months ago

serjek commented 3 months ago

I am thinking about something like this:

class Db extends tink.sql.Database {
  @:table @:cache var DataPoints:DataPoints;
}

where @:cache will generate an internal class like this:

class Memcached<T> implements coconut.data.Model {
    @:constant var loadData:Void->Promise<Array<T>>;
    @:constant var dbInsertOne:T->Promise<Noise> = @byDefault _ -> Promise.lift(new Error("not implemented"));
    @:constant var dbInsertMany:Array<T>->Promise<Noise> = @byDefault _ -> Promise.lift(new Error("not implemented"));

    @:loaded var loadedData:Vector<T> = loadData().next(Vector.fromArray);
    @:computed var data:Vector<T> = loadedData.or(Vector.empty()) & cachedData;
    @:computed var isLoaded:Bool = loadedData != Loading;
    @:observable var cachedData:Vector<T> = Vector.empty();

    @:transition function insertOne(v:T) {
        dbInsertOne(v).handle(v -> switch v {
            case Success(_):
            case Failure(e): throw e;
        });
        return {cachedData: cachedData & v}
    }
    @:transition function insertMany(v:Array<T>) {
        dbInsertMany(v).handle(v -> switch v {
            case Success(_):
            case Failure(e): throw e;
        });
        return {cachedData: cachedData & Vector.fromArray(v)}
    }
}

and declared like this

var memcachedDataPoints:Memcached<DataPoints> = new Memcached({
  loadData: db.DataPoints.select().all,
  dbInsertOne: db.DataPoints.insertOne,
  dbInsertMany: db.DataPoints.insertMany
});

And then data still can be accessed (set/get) directly through table declaration, with data flow processed by memcached middleware.

Pro: easy to use application level cache with unified and clean api Con: extra dependency (coconut)