Busy Handling / Tracker store status

Example at the bottom that uses libtracker-sparql should be added

About

When the tracker-store is started, it will check the integrity of the DBs and recover them from a journal in case of troubles. During that time (it can take up to minutes) queries cannot be answered and clients can receive timeouts. This documents describes the Status DBus API in the store that allow applications to follow the status of the daemon.

This API is relevant for applications using directly the DBus API of tracker-store. Applications using libtracker-sparql convenience library don't need to care, the library already makes use of it internally.

All the methods/signals described here are under the org.freedesktop.Tracker1.Status interface, in the org/freedesktop/Tracker1/Status object in DBus.

Wait for the store to be ready (synchronous/blocking approach)

The method Wait will block and return when the store is ready. This is extremely simple in the code (just one call) but has several draw backs: if the journal replaying take long this call will fail because of a dbus timeout (that timeout can be tuned in the client side); the call blocks the client application which usually is not acceptable for UI apps.

Wait for the store to be ready (asynchronous approach)

To follow asynchronously the status of the store, there is the Progress signal. It comes with two parameters, a string with the status and a double with the progress between 0 and 1. When the store is ready status is "Idle" and Progress is "1".

Query the status of the daemon (Poll)

At any moment, it is possible to know the status of the daemon with the methods !GetProgress()} and !GetStatus(). The semantics of progress and status is the same as in the signals signature.

Daemon boot sequence in the API

| -> tracker-store process is started
|
| -> /org/freedesktop/Tracker1/Status object and interfaces appear in DBus
|    
|  -> Status: Journal replaying - Progress: from 0 to 1
|
|  -> Status: Idle  - Progress: 1.0   *** STORE READY ***
|

Normal D-Bus access

With Tracker 0.9 you can fire off a query while the tracker-store is busy replaying the journal or restoring a backup.

The tracker-store will queue the query until after its busy state is finished.

You still need to at the clientside set the DBus timeout, and you can get a progress indicator using a signal that'll be emitted to you by tracker-store.

Just launch the query and let the RDF store queue it for you

[DBus (name = "org.freedesktop.Tracker1.Resources")]
private interface Resources : GLib.Object {
        [DBus (name = "SparqlQuery", timeout = 99999999999)]
        public abstract async string[,] sparql_query_async (string query) throws DBus.Error;
}
[DBus (name = "org.freedesktop.Tracker1.Status")]
private interface Status: GLib.Object {
        public signal void progress (string status, double progress);
}

public class TestApp {
        static DBus.Connection connection;
        static Resources resources_object;
        static Status status_object;
        MainLoop loop;
        bool initialized = false;

        public TestApp ()
        requires (!initialized) {
                connection = DBus.Bus.get (DBus.BusType.SESSION);
                resources_object = (Resources) connection.get_object ("org.freedesktop.Tracker1",
                                                                      "/org/freedesktop/Tracker1/Resources",
                                                                      "org.freedesktop.Tracker1.Resources");
                status_object = (Status) connection.get_object ("org.freedesktop.Tracker1",
                                                                "/org/freedesktop/Tracker1/Status",
                                                                "org.freedesktop.Tracker1.Status");
                status_object.progress.connect (on_status_cb);
                initialized = true;
        }

        void on_status_cb (string status, double progress) {
                print ("%s: %f\n", status, progress);
        }

        async void do_query_tests_async () {
                string[,] results = yield resources_object.sparql_query_async ("SELECT ?u { ?u a rdfs:Resource }");
                foreach (string res in results) {
                        print ("%s\n", res);
                }
        }

        async void do_async_query_tests () {
                yield do_query_tests_async ();
                loop.quit ();
        }

        bool in_mainloop () {
                do_async_query_tests ();
                return false;
        }
        public int run () {
                loop = new MainLoop (null, false);
                Idle.add (in_mainloop);
                loop.run ();
                return res;
        }
}

int main (string[] args) {
        TestApp app = new TestApp ();
        return app.run ();
}

Wait for the RDF store yourself

[DBus (name = "org.freedesktop.Tracker1.Resources")]
private interface Resources : GLib.Object {
        [DBus (name = "SparqlQuery", timeout = 99999999999)]
        public abstract async string[,] sparql_query_async (string query) throws DBus.Error;
}

[DBus (name = "org.freedesktop.Tracker1.Status")]
private interface Status: GLib.Object {
        public signal void progress (string status, double progress);
        public abstract double get_progress () throws DBus.Error;
        public abstract string get_status () throws DBus.Error;
}

public class TestApp {
        static DBus.Connection connection;
        static Resources resources_object;
        static Status status_object;
        int res = -1;
        int users = 0;
        MainLoop loop;
        bool initialized = false;
        bool ready = false;

        public TestApp ()
        requires (!initialized) {
                try {
                        double progress;
                        string status;
                        connection = DBus.Bus.get (DBus.BusType.SESSION);
                        resources_object = (Resources) connection.get_object ("org.freedesktop.Tracker1",
                                                                              "/org/freedesktop/Tracker1/Resources",
                                                                              "org.freedesktop.Tracker1.Resources");
                        status_object = (Status) connection.get_object ("org.freedesktop.Tracker1",
                                                                        "/org/freedesktop/Tracker1/Status",
                                                                        "org.freedesktop.Tracker1.Status");

                        status_object.progress.connect (on_status_cb);
                        progress = status_object.get_progress ();
                        status = status_object.get_status ();
                        ready = (progress == 1.0 && status == "Idle");
                } catch (DBus.Error e) {
                        warning ("Could not connect to D-Bus service: %s", e.message);
                        initialized = false;
                        res = -1;
                        return;
                }
                initialized = true;
        }
        void on_status_cb (string status, double progress) {
                print ("%s: %f\n", status, progress);
                // Don't use status here, it'll be "Journal replaying" when progress = 1
                if (progress == 1.0) {
                        ready = true;
                }
        }
        async void do_query_tests_async () {
                try {
                        int cnt = 0;
                        string[,] results = yield resources_object.sparql_query_async ("SELECT ?u { ?u a rdfs:Resource }");
                        foreach (string res in results) {
                                cnt++;
                        }
                        print ("%s: Saw %d strings in result\n", test_name, cnt);
                } catch (GLib.Error e) {
                        print ("Fail: %s\n", e.message);
                        res = -1;
                }
        }
        void check_shutdown () {
                users--;
                if (users == 0) {
                        print ("Async tests done, now I can quit the mainloop\n");
                        loop.quit ();
                }
        }
        async void jumper_async () {
                yield do_query_tests_async ();
                check_shutdown ();
        }
        bool test_ready () {
                if (ready)
                        jumper_async ();
                return !ready;
        }

        bool in_mainloop () {
                users++;
                if (!ready)
                        Timeout.add (1, test_ready);
                else
                        test_ready ();
                return false;
        }

        public int run () {
                loop = new MainLoop (null, false);
                Idle.add (in_mainloop);
                loop.run ();
                return res;
        }
}

int main (string[] args) {
        TestApp app = new TestApp ();
        return app.run ();
}

Direct access

When using direct-access the library libtracker-sparql will resolve busy handling for you. More information will follow soon.

Attic/Tracker/Documentation/BusyHandling (last edited 2023-08-14 12:50:01 by CarlosGarnacho)