question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Async/Transaction/Something problem

See original GitHub issue

I’m trying to cache data in a simple cacheService module I built for my angular app.

The data gets returned from the WebApi. I “put” the data into a store and return a promise so my calling client can continue the process.

From here I want to access the store where I just saved data and apply various filters.

I think the data is not ready for me to query because I can’t seem to get to it.

I can’t seem to find much documentation on “put” to understand how to know when it’s done saving so that I know when it’s safe to fetch the data again. All the examples fetch the data immediately as if it were a synchronous call. Maybe it is. I’m new so I don’t quite understand yet.

All I know is I spent all day on this and now all night.

What happens is when I press a button to retrieve data I get nothing back into the UI, when I have not previously requested the data. The second time I press my button I get data but it’s the data from the original request and not the newest data.

Summary:

  1. Fetch data via Web Api and place it into an IndexedDB data store with YDN-DB
  2. Return control to my UI
  3. UI executes a query against the IndexedDB data store.
  4. No data is to be found.

Thoughts?

Here is my cacheService. It’s probably a mess to a veteran but again, I’m new.

mashupApp.service(‘cacheService’, function ($http, $q, $log) {

var isCacheStale = function (cacheName, minutes) {
    // dbCache: is 'mashCache'
    // store: No store is provided but might be added later to remove constaint 'mashCacheAge'
    // subject: is the name of the cache being evaluated.
    // minutes: is the number of minutes before the cache is considered stale.

    var deferred = $q.defer();
    var result = true;

    // get milliseconds version of minutes.
    var ageToleranceMilliseconds = (minutes * 60) * 1000;
    var currentDateMilliseconds = new Date().getTime();

    // checking dbCacheReady because, if a page loads to quickly, before the cache database is ready
    // then accessing it will cause an error.
    (function wait() {
        if (dbCacheReady) {

            // The database is ready so go ahead and begin checking for stale data.
            try {
                dbCache.executeSql('SELECT * FROM mashCacheAge WHERE id = \'' + cacheName + '\'').then(function (record) {
                    $log.log('mashCacheAge record for: [ ' + cacheName + ' ] cache.');
                    $log.log(record);

                    // if no record is returned then it is considered stale.
                    var recordCount = record.length;
                    if (recordCount > 0) {
                        var durationMilliseconds = currentDateMilliseconds - record[0].updatedDate;

                        // Check if the data is stale.
                        if (durationMilliseconds > ageToleranceMilliseconds) {
                            result = true;
                        }
                        else { result = false; }

                        deferred.resolve(result);
                    }
                    else {
                        // no records found so this cache is considered stale.
                        deferred.resolve(true);
                    }
                });
            }
            catch (e) {
                // no data store for the cache was found so it is considered stale.
                deferred.resolve(true);
            }

        } else {
            // Giving the cache database a moment to set up.
            setTimeout(wait, 500);
        }
    })();

    return deferred.promise;
};

// Updates the age of any cacheName.
var updateCacheAge = function (cacheName) {
    // updates the mashCacheAge.  This helps track how old/stale a cache is.

    var cacheJSON = {
        id: cacheName,
        updatedDate: new Date().getTime()
    };

    dbCache.put({ name: 'mashCacheAge', keyPath: 'id' }, cacheJSON);

}

var getCache = function (cacheName) {
    var deferred = $q.defer();

    // waiting for dbCacheReady because accessing the cache to early causes an error.
    (function wait() {
        if (dbCacheReady) {

            dbCache.executeSql('SELECT * FROM \'' + cacheName + '\'').then(function (record) {
                deferred.resolve(record);
            });

        } else { setTimeout(wait, 500); }
    })();

    return deferred.promise;
}

// --------------------------------------------------------------------------------
// If the machCache is too old then all the data is wiped out and the user starts again.
// --------------------------------------------------------------------------------
var minutesInOneWeek = 10080;  // 10080 = 1 week
isCacheStale('mashCacheStart', minutesInOneWeek).then(function (result) {
    //alert(result);
    if (result) {
        // TODO: Add this to mashup logging.
        // the mashCache is too old and needs cleared.
        //ydn.db.deleteDatabase('mashCache');
        //setTimeout(function () { alert('mashCache was stale so deleted.'); }, 1);
        //dbCache.clear();
        // The first time through there will be no objects and this clear will fail. 
        // TODO: update this to could objects before calling
        try { dbCache.clear(); } catch (e) { }
        $log.log('mashCache was stale so deleted.');
        updateCacheAge('mashCacheStart');
    };
});
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------

return {
    // Retrieve cache
    getCache: function (cacheName) {
        var deferred = $q.defer();
        getCache(cacheName).then(function (data) {
            deferred.resolve(data);
        });
        return deferred.promise;
    },
    getData: function (cacheName, schema, webApiUrl, staleMinutes) {

        var deferred = $q.defer();
        // var cacheName = 'getItems2';

        // Check if the cache is stale.
        isCacheStale(cacheName, staleMinutes).then(function (cacheIsStale) {
            // If cache stale then get new data.
            if (cacheIsStale) {
                // cache has become stale so retrieving fresh data.
                $http.get(webApiUrl, { withCredentials: true })
                    .success(function (data) {

                        //#region

                        // -------------------------------------------------------------------------------
                        // Schema will be passed in so that it can be generated on the fly.
                        // When a schema for an object store changes it will break and cause
                        // an error.  We will look for that error and simply delete the
                        // indexedDB database so the next time this is caused the new schema
                        // can take hold.  This doesn't seem ideal but the impact is only
                        // to performance when schemas change for cached data.
                        // -------------------------------------------------------------------------------
                        // Normally the schema is defined up front and versioned.  For the cache we want
                        // developers to have one less thing to consider and allow the mashup core
                        // to be less coupled to the caching needs of other mashup applications.
                        // -------------------------------------------------------------------------------

                        //#endregion

                        try {
                            // add data to cache
                            dbCache.put(schema, data);
                            // updateCacheAge
                            updateCacheAge(cacheName);
                        }
                        catch (err) {
                            $log.log(err);
                            indexedDB.deleteDatabase('mashCache');
                            $log.log("IndexedDB error on updating a cache. Deleted database to allow new schema.");
                            alert("IndexedDB error on updating a cache. Deleted database to allow new schema.");

                        }

                        // return web api data to the client
                        // async alert() so the performance perception isn't affected.
                        //setTimeout(function () { alert('web api data'); }, 1);
                        deferred.resolve(data);
                    })
                .error(function () {
                    // if the call fails then return the current cache.
                    // TODO: make an async call to let someone know a service failed?
                    alert("Web Api Error");
                    getCache(cacheName).then(function (data) {
                        // async alert()
                        // setTimeout(function () { alert('cache data'); }, 1);
                        deferred.resolve(data);
                    });
                });
            } else {
                // cached data is still good so return it.
                getCache(cacheName).then(function (data) {
                    // async alert()
                    //setTimeout(function () { alert('cache data'); }, 1);
                    deferred.resolve(data);
                });
            }
        });
        return deferred.promise;
    }

};

});

Here is the client code. Or at least it’s a client to the service. (portion)

},

    getExample4items: function (staleMinutes, id) {
        var deferred = $q.defer();

        var cacheName = 'MashupExamples_example4items';
        var schema = { name: cacheName, keyPath: 'id' };
        var webApiUrl = 'http://localhost:50000/api/MashupExamples/Items/';

        try {
            cacheService.getData(cacheName, schema, webApiUrl, staleMinutes).then(function (data) {

                try {

                    var record = _.where(data, { 'id': parseInt(id) });
                    deferred.resolve(record);

                } catch (e) { $log.log(e); }

            });
        } catch (e) { $log.log(e); }


        return deferred.promise;
    },

You’ll notice that I’m passing in the schema with the call. That’s because I don’t want to define a schema for the entire application up front and deal with all the versioning. I seem to have been able to work around this.

Thanks, Bob

Issue Analytics

  • State:closed
  • Created 9 years ago
  • Comments:9 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
yathitcommented, Aug 29, 2014

The bug has been fixed in rel 1.0.3. http://jsfiddle.net/kyawtuns/Ya3Cv/1/

0reactions
robertdunawaycommented, Sep 9, 2014

That was very fast. I’ll get the new code and test it. I was on vacation last week or I would have done this sooner.

I didn’t know you were the author of YDN-DB. Great job and I love the library. Thank you.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Transaction Scope not rolling back with async/await
In your example you first rolling transaction back (by disposing it), and then calling Complete. Complete it before closing connection. Also try ...
Read more >
TransactionScope and Async/Await. Be one with the flow
This is a big problem, as it makes writing asynchronous code involving transactions extremely error-prone. The good news is that as part of ......
Read more >
Exceptions during async transactions are failing silently #2130
So I've ran into a very frustrating error. Exceptions during asynchronous transactions aren't getting reported. No signs of stacktrace.
Read more >
Error: Cannot create asynchronous query while in a write ...
I have researched this and I believe that it's saying that while the database is being written to, a component can't create a...
Read more >
Synchronous and asynchronous transactions - Huihoo
Some Couchbase SDKs support both synchronous and asynchronous transactions. In the case of synchronous transactions, your application pauses its execution ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found