Beispiel #1
0
        private async Task <OpResponse> ProcessReplace(RequestOp req)
        {
            OpResponse?opResponse = await Origin.ProcessRequest(req);

            await StoreInPreviousCaches(opResponse);

            return(opResponse !);
        }
Beispiel #2
0
        public Task <OpResponse> GetResponse <M>(long id, int?freshnessSeconds = null) where M : class
        {
            var req = RequestOp.GetOp <M>(
                id,
                NowMs,
                freshnessSeconds ?? Config.DefaultFreshnessSeconds
                );

            return(ProcessRequest(req));
        }
Beispiel #3
0
        public Task <OpResponse> QueryResponse <M>(string key, object criteria, int?freshnessSeconds = null)
        {
            var req = RequestOp.QueryOp <M>(
                key,
                criteria,
                NowMs,
                freshnessSeconds ?? Config.DefaultFreshnessSeconds
                );

            return(ProcessRequest(req));
        }
Beispiel #4
0
        public async Task <OpResponse> Fetch(RequestOp requestOp)
        {
            if (requestOp.Type != typeof(Model))
            {
                throw new Exception("requestOp.Type != typeof(Model)");
            }
            switch (requestOp.Verb)
            {
            case RequestVerb.Get:
                var id = (IdType?)CascadeTypeUtils.ConvertTo(typeof(IdType), requestOp.Id);                           //  ((IdType)requestOp.Id)!;
                if (id == null)
                {
                    throw new Exception("Unable to get right value for Id");
                }

                if (models.ContainsKey(id))
                {
                    return(new OpResponse(
                               requestOp,
                               Cascade.NowMs,
                               connected: true,
                               exists: true,
                               result: models[id].Item1,
                               arrivedAtMs: models[id].Item2
                               ));
                }
                else
                {
                    return(new OpResponse(
                               requestOp,
                               Cascade.NowMs,
                               connected: true,
                               exists: false,
                               result: null,
                               arrivedAtMs: null
                               ));
                }
                break;

            case RequestVerb.Query:
                if (collections.ContainsKey(requestOp.Key !))
                {
                    return(new OpResponse(
                               requestOp,
                               Cascade.NowMs,
                               connected: true,
                               exists: true,
                               result: collections[requestOp.Key !].Item1,
Beispiel #5
0
 public OpResponse(
     RequestOp requestOp,
     long timeMs,
     bool connected,
     bool exists,
     long?arrivedAtMs,
     object?result
     )
 {
     RequestOp   = requestOp;
     TimeMs      = timeMs;
     Connected   = connected;
     Exists      = exists;
     ArrivedAtMs = arrivedAtMs;
     Result      = result;
 }
Beispiel #6
0
        private Task <OpResponse> ProcessRequest(RequestOp req)
        {
            switch (req.Verb)
            {
            case RequestVerb.Get:
            case RequestVerb.Query:
                return(ProcessReadOrQuery(req));

            case RequestVerb.Create:
                return(ProcessCreate(req));

            case RequestVerb.Replace:
                return(ProcessReplace(req));

            default:
                throw new ArgumentException("Unsupported verb");
            }
        }
Beispiel #7
0
        public async Task TestCollections()
        {
            var path = System.IO.Path.GetTempFileName();
            var conn = new SQLite.SQLiteAsyncConnection(path);
            var db   = new TestDatabase(conn);
            await db.Reset();

            var sqliteThingCache = new SqliteClassCache <Parent, long>(db);
            await sqliteThingCache.Setup();

            var sqliteGadgetCache = new SqliteClassCache <Child, string>(db);
            await sqliteGadgetCache.Setup();

            var cache = new ModelCache(aClassCache: new Dictionary <Type, IModelClassCache>()
            {
                { typeof(Parent), sqliteThingCache },
                { typeof(Child), sqliteGadgetCache }
            });
            var cascade = new CascadeDataLayer(origin, new ICascadeCache[] { cache }, new CascadeConfig()
            {
                DefaultFreshnessSeconds = 1
            });

            var collection_key = "my_things";
            var ids            = ImmutableArray.Create <object>(1, 2, 3);
            var response       = await cache.Fetch(RequestOp.QueryOp <Parent>(collection_key, new JsonObject(), 0));

            Assert.AreEqual(false, response.Exists);
            Assert.AreEqual(null, response.Result);
            await cache.StoreCollection(typeof(Parent), collection_key, ids, 0);

            response = await cache.Fetch(RequestOp.QueryOp <Parent>(collection_key, null, 0));

            Assert.IsTrue(CascadeTypeUtils.IsEqualEnumerable(ids, response.ResultIds));

            response = await cache.Fetch(RequestOp.QueryOp <Parent>("not_my_key", null, 0));

            Assert.IsFalse(response.Exists);

            response = await cache.Fetch(RequestOp.QueryOp <Child>(collection_key, null, 0));

            Assert.IsFalse(response.Exists);
        }
Beispiel #8
0
        private async Task <OpResponse> ProcessReadOrQuery(RequestOp requestOp)
        {
            object?       value;
            ICascadeCache?layerFound = null;
            OpResponse?   opResponse = null;

            // try each cache layer
            foreach (var layer in CacheLayers)
            {
                var res = await layer.Fetch(requestOp);

                if (res.PresentAndFresh())
                {
                    layerFound = layer;
                    opResponse = res;
                    break;
                }
            }
            // if still not found, try origin
            if (opResponse == null)
            {
                opResponse = await Origin.ProcessRequest(requestOp);
            }

            if (requestOp.Verb == RequestVerb.Query && opResponse.IsIdResults)
            {
                var modelResponses = await GetModelsForIds(requestOp.Type, requestOp.FreshnessSeconds ?? Config.DefaultFreshnessSeconds, opResponse.ResultIds);

                // don't need to do this because the get above will achieve the same
                // foreach (var modelResponse in modelResponses) {
                //  await StoreInPreviousCaches(opResponse, layerFound);
                // }
                await StoreInPreviousCaches(opResponse, layerFound);                                                    // just store ResultIds

                opResponse = opResponse.withChanges(result: modelResponses.Select(r => r.Result).ToImmutableArray());   // modify the response with models instead of ids
            }
            else
            {
                await StoreInPreviousCaches(opResponse, layerFound);
            }
            return(opResponse !);
        }
Beispiel #9
0
        //
        //
        // // special case for enums
        // if (targetType.IsEnum) {
        //  // we could be going from an int -> enum so specifically let
        //  // the Enum object take care of this conversion
        //  if (value != null) {
        //      value = Enum.ToObject(targetType, value);
        //  }
        // }
        // else {
        //  // returns an System.Object with the specified System.Type and whose value is
        //  // equivalent to the specified object.
        //  value = Convert.ChangeType(value, targetType);
        // }

        // set the value of the property
        //  propertyInfo.SetValue(target, value, null);
        // }



        private async Task processHasMany(SuperModel model, Type modelType, PropertyInfo propertyInfo, HasManyAttribute attribute)
        {
            var propertyType = CascadeTypeUtils.DeNullType(propertyInfo.PropertyType);
            var isEnumerable = (propertyType?.Implements <IEnumerable>() ?? false) && propertyType != typeof(string);
            var foreignType  = isEnumerable ? CascadeTypeUtils.InnerType(propertyType !) : null;

            foreignType = foreignType != null?CascadeTypeUtils.DeNullType(foreignType) : null;

            if (foreignType == null)
            {
                throw new ArgumentException("Unable to get foreign model type. Property should be of type ImmutableArray<ChildModel>");
            }

            // var typeLayers = GetTypeLayers(propertyType);
            // var nonNullableType = DeNullType(propertyType);
            //
            //  typeLayers.FirstOrDefault(t => t.Name != "Nullable`1");
            //
            // var foreignType = isEnumerable ? propertyType.GetGenericArguments().FirstOrDefault() : null;
            object modelId = CascadeTypeUtils.GetCascadeId(model);
            var    key     = $"HasMany__{foreignType.Name}__{attribute.ForeignIdProperty}__{modelId}";

            var requestOp = new RequestOp(
                NowMs,
                foreignType,
                RequestVerb.Query,
                null,
                null,
                0,
                new Dictionary <string, object>()
            {
                [attribute.ForeignIdProperty] = modelId
            },
                key
                );
            var opResponse = await ProcessRequest(requestOp);

            CascadeTypeUtils.SetModelCollectionProperty(model, propertyInfo, opResponse.Results);
            //propertyInfo.SetValue(model,opResponse.Results);
        }
Beispiel #10
0
        private async Task processBelongsTo(object model, Type modelType, PropertyInfo propertyInfo, BelongsToAttribute attribute)
        {
            var foreignModelType = CascadeTypeUtils.DeNullType(propertyInfo.PropertyType);
            var idProperty       = modelType.GetProperty(attribute.IdProperty);
            var id = idProperty.GetValue(model);

            if (id == null)
            {
                return;
            }

            var requestOp = new RequestOp(
                NowMs,
                foreignModelType,
                RequestVerb.Get,
                id,
                freshnessSeconds: Config.DefaultFreshnessSeconds
                );
            var opResponse = await ProcessRequest(requestOp);

            SetModelProperty(model, propertyInfo, opResponse.Result);
        }
Beispiel #11
0
        public async Task <OpResponse> Fetch(RequestOp requestOp)
        {
            if (requestOp.Type != typeof(Model))
            {
                throw new Exception("requestOp.Type != typeof(Model)");
            }
            switch (requestOp.Verb)
            {
            case RequestVerb.Get:
                var id = (IdType?)CascadeTypeUtils.ConvertTo(typeof(IdType), requestOp.Id);                         //  ((IdType)requestOp.Id)!;
                if (id == null)
                {
                    throw new Exception("Unable to get right value for Id");
                }

                if (await _database.Exists <Model>(id))
                {
                    var meta = await _database.Get <CascadeModelMeta>(CascadeModelMeta.GenerateId <Model>(id));

                    return(new OpResponse(
                               requestOp,
                               Cascade !.NowMs,
                               connected: true,
                               exists: true,
                               result: await _database.Get <Model>(id),
                               arrivedAtMs: meta?.arrived_at
                               ));
                }
                else
                {
                    return(new OpResponse(
                               requestOp,
                               Cascade !.NowMs,
                               connected: true,
                               exists: false,
                               result: null,
                               arrivedAtMs: null
                               ));
                }
                break;

            case RequestVerb.Query:
                var collection = await _database.Get <CascadeCollection>(CascadeCollection.GenerateId <Model>(requestOp.Key !));

                if (collection != null)
                {
                    return(new OpResponse(
                               requestOp,
                               Cascade !.NowMs,
                               connected: true,
                               exists: true,
                               result: collection.objectIds,
                               arrivedAtMs: collection.arrived_at
                               ));
                }
                else
                {
                    return(new OpResponse(
                               requestOp,
                               Cascade !.NowMs,
                               connected: true,
                               exists: false,
                               result: null,
                               arrivedAtMs: null
                               ));
                }
                break;

            default:
                throw new NotImplementedException($"Unsupported {requestOp.Verb}");
            }
        }
Beispiel #12
0
        public async Task ReadWithModelCachesMultitest()
        {
            var thingModelStore1 = new ModelClassCache <Parent, long>();
            var cache1           = new ModelCache(aClassCache: new Dictionary <Type, IModelClassCache>()
            {
                { typeof(Parent), thingModelStore1 }
            });
            var thingModelStore2 = new ModelClassCache <Parent, long>();
            var cache2           = new ModelCache(aClassCache: new Dictionary <Type, IModelClassCache>()
            {
                { typeof(Parent), thingModelStore2 }
            });

            // read from origin
            var cascade = new CascadeDataLayer(origin, new ICascadeCache[] { cache1, cache2 }, new CascadeConfig()
            {
                DefaultFreshnessSeconds = 1
            });
            var thing1 = await cascade.Get <Parent>(5);

            Assert.AreEqual(5, thing1 !.id);
            Assert.AreEqual(cascade.NowMs, thing1.updatedAtMs);

            // should also be in both caches
            var store1ThingResponse = await thingModelStore1.Fetch(RequestOp.GetOp <Parent>(5, cascade.NowMs));

            Assert.AreEqual((store1ThingResponse.Result as Parent) !.id, 5);
            Assert.AreEqual(cascade.NowMs, (store1ThingResponse.Result as Parent) !.updatedAtMs);
            var store2ThingResponse = await thingModelStore2.Fetch(RequestOp.GetOp <Parent>(5, cascade.NowMs));

            Assert.AreEqual((store2ThingResponse.Result as Parent) !.id, 5);
            Assert.AreEqual(cascade.NowMs, (store2ThingResponse.Result as Parent) !.updatedAtMs);

            origin.IncNowMs();

            // freshness=5 allows for cached version
            var thing2 = (await cascade.Get <Parent>(5, freshnessSeconds: 5)) !;

            Assert.AreEqual(thing1.updatedAtMs, thing2.updatedAtMs);

            // freshness=0 doesn't allow for cached version
            var thing3 = (await cascade.Get <Parent>(5, freshnessSeconds: 0)) !;

            Assert.AreEqual(origin.NowMs, thing3.updatedAtMs);

            // caches should also be updated
            store1ThingResponse = await thingModelStore1.Fetch(RequestOp.GetOp <Parent>(5, cascade.NowMs));

            Assert.AreEqual(origin.NowMs, (store1ThingResponse.Result as Parent) !.updatedAtMs);
            store2ThingResponse = await thingModelStore2.Fetch(RequestOp.GetOp <Parent>(5, cascade.NowMs));

            Assert.AreEqual(origin.NowMs, (store2ThingResponse.Result as Parent) !.updatedAtMs);

            origin.IncNowMs(2000);

            // freshness=2 should allow for cached version
            var thing4 = (await cascade.Get <Parent>(5, freshnessSeconds: 2)) !;

            Assert.AreEqual(thing3.updatedAtMs, thing4.updatedAtMs);

            // freshness=1 should get fresh version
            var thing5 = (await cascade.Get <Parent>(5, freshnessSeconds: 1)) !;

            Assert.AreEqual(origin.NowMs, thing5.updatedAtMs);

            origin.IncNowMs(1000);

            // clear cache1, freshnessSeconds=1 should return value from cache2 and update cache1
            await cache1.Clear();

            var thing6 = (await cascade.Get <Parent>(thing4.id, freshnessSeconds: 1)) !;                                // should get cache2 version

            Assert.AreEqual(thing6.updatedAtMs, thing5.updatedAtMs);
            store1ThingResponse = await thingModelStore1.Fetch(RequestOp.GetOp <Parent>(5, cascade.NowMs));

            Assert.AreEqual(thing6.updatedAtMs, (store1ThingResponse.Result as Parent) !.updatedAtMs);
        }
Beispiel #13
0
        public async Task TestMetadata()
        {
            var path = System.IO.Path.GetTempFileName();
            var conn = new SQLite.SQLiteAsyncConnection(path);
            var db   = new TestDatabase(conn);
            await db.Reset();

            var sqliteThingCache = new SqliteClassCache <Parent, long>(db);
            await sqliteThingCache.Setup();

            var sqliteGadgetCache = new SqliteClassCache <Child, string>(db);
            await sqliteGadgetCache.Setup();

            var cache1 = new ModelCache(aClassCache: new Dictionary <Type, IModelClassCache>()
            {
                { typeof(Parent), sqliteThingCache },
                { typeof(Child), sqliteGadgetCache }
            });

            // read from origin
            var cascade = new CascadeDataLayer(origin, new ICascadeCache[] { cache1 }, new CascadeConfig()
            {
                DefaultFreshnessSeconds = 1
            });

            var thing5 = new Parent()
            {
                id     = 5,
                colour = "red"
            };
            var thing5ArrivedAt = cascade.NowMs;
            await sqliteThingCache.Store(thing5.id, thing5, thing5ArrivedAt);

            origin.IncNowMs();

            var gadget6 = new Child()
            {
                id     = "abc",
                weight = 2.5,
                power  = 9.2
            };
            var gadget6ArrivedAt = cascade.NowMs;
            await sqliteGadgetCache.Store(gadget6.id, gadget6, gadget6ArrivedAt);

            origin.IncNowMs();

            var opResponse = await sqliteThingCache.Fetch(RequestOp.GetOp <Parent>(thing5.id, cascade.NowMs));

            var loaded5 = (opResponse.Result as Parent) !;

            Assert.AreEqual(thing5ArrivedAt, opResponse.ArrivedAtMs);
            Assert.AreEqual(thing5.colour, loaded5.colour);

            opResponse = await sqliteGadgetCache.Fetch(RequestOp.GetOp <Child>(gadget6.id, cascade.NowMs));

            var loaded6 = (opResponse.Result as Child) !;

            Assert.AreEqual(gadget6ArrivedAt, opResponse.ArrivedAtMs);
            Assert.AreEqual(gadget6.weight, loaded6.weight);

            await sqliteGadgetCache.Clear();

            // thing unaffected
            opResponse = await sqliteThingCache.Fetch(RequestOp.GetOp <Parent>(thing5.id, cascade.NowMs));

            Assert.NotNull(opResponse.Result);
            Assert.AreEqual(thing5ArrivedAt, opResponse.ArrivedAtMs);

            // gadget cleared including metadata
            opResponse = await sqliteGadgetCache.Fetch(RequestOp.GetOp <Child>(gadget6.id, cascade.NowMs));

            Assert.IsNull(opResponse.Result);
            Assert.IsNull(opResponse.ArrivedAtMs);
            var meta6 = await db.Get <CascadeModelMeta>(CascadeModelMeta.GenerateId <Child>(6));

            Assert.IsNull(meta6);
        }