Data Freshness Process

Every night, the implementation of HDX freshness in Python reads all the datasets from HDX (using the HDX Python library) and then iterates through them one by one performing a sequence of steps.

At a high level it does the following:

Compares expected update frequency with time since last update to give status: fresh, due, overdue or delinquent.

eg. update frequency = every week, last updated 5 days ago:

7 days > 5 days => Fresh  

If last updated:

  0-6 days ago => Fresh                7-13 days ago => Due

  14-21 days ago => Overdue       >21 days ago => Delinquent



The following goes into more detail:



The method of determining whether a resource is updated depends upon where the file is hosted. If it is in HDX ie. in the file store, then the update time is reflected in the HDX dataset metadata. If it is hosted externally, then it is not as straightforward to find out if the file pointed to by a url has changed. It is possible to use the last_modified field that is returned from an HTTP GET request provided the hosting server supports it or not. (For performance reasons, we open a stream, so that we have the option to only read the header information rather than the entire file). If it is a link to a file on a server like Apache or Nginx then the field may exist, but if it is a url that generates a result on the fly, then it does not.

An alternative approach discussed with researchers at the University of Vienna is to download the external urls, hash the files and compare the hashes. Since there can be temporary connection and download issues with urls, the code has multiple retry functionality with increasing delays. Also as there are many requests to be made, rather than perform them one by one, they are executed concurrently using the asynchronous functionality that has been added to the most recent versions of Python. The researchers had done some calculations and asserted that it would be too resource intensive to hash all of the files daily mainly due to the time to download all of the urls. However, by applying logic so that we do not need to download all of the files every day, we have restricted the load significantly.

The steps involved in data freshness are:

  1. Read the dataset last modified date from HDX (which updates if the data of any resource changes) and the resource last modified dates (which updates if the resource data is changed so should be encapsulated by the dataset last modified date).

  2. Load the previous run's last modified dates and replace dates if more recent.

  3. Check the dataset's resources. If any have not been hashed for 30 days and the count of number of datasets unhashed in 30 days < 1/30 the number of resources, then we mark the resources as to be hashed.

  4. Get the dataset's update frequency if it has one. If that update frequency is Never (-1 in API), Live (0) or As needed (-2)  then the dataset is always fresh. 

  5. If the dataset is not fresh based on metadata, then examine the urls of the resources. If they are internal urls (data.humdata.org - the HDX filestore, manage.hdx.rwlabs.org - CPS) then there is no further checking that can be done because when the files pointed to by these urls update, the HDX metadata also updates.

  6. If the url is externally hosted, open an HTTP GET request to the file and check the header returned for the Last-Modified field. If that field exists, then read the date and time from it and if it is more recent than the resource last modification date, replace it.

  7. If the resource is not fresh by this measure then:

    1. Download the file and calculate an MD5 hash for it.

    2. Check if the hash has changed compared with hash from the previous run stored in the database.

  8. There are some resources where the hash changes constantly because they connect to an api which generates a file on the fly. To identify these, download and hash again and check if the hash changes in the few seconds since the previous hash calculation. If so, we cannot determine freshness.

  9. If the hash has changed since the last run, but doesn't change constantly (API), store the hash and replace the resource last modified date with the current date (now).

  10. Record the dataset and resources last modified dates.

  11. Calculate freshness by measuring the difference between the current date and last modified date and comparing with the update frequency, setting a status: fresh, due, overdue or delinquent.



The flowchart below represents the logical flow for each dataset and resource in HDX and occurs nightly. It is slightly out of date since update frequency “Adhoc” is now “As needed” and the HTTP header last modified field is no longer used as it proved unreliable.

 

Data freshness is available from Docker image: https://hub.docker.com/r/mcarans/hdx-data-freshness/. The code for the implementation is here: https://github.com/OCHA-DAP/hdx-data-freshness. It has tests with a high level of coverage.

It produces some simple metrics eg. from a production run:

*** Resources ***

  • total: 149308 *,
    api: 14,
    api,error: 46,
    error: 100,
    filestore: 2088,
    filestore,error: 1,
    filestore,hash: 2,
    filestore,same hash: 5,
    firstrun: 1,
    hash: 33,
    hash,error: 2,
    internal-filestore: 9,
    internal-firstrun: 6,
    internal-nothing: 7788,
    nothing: 138926,
    repeat hash: 1,
    same hash: 266,
    same hash,error: 20

*** Datasets ***

  • total: 22160 *,
    0: Fresh, Updated filestore: 4,
    0: Fresh, Updated filestore,error: 1,
    0: Fresh, Updated filestore,filestore,hash: 2,
    0: Fresh, Updated filestore,hash: 2,
    0: Fresh, Updated filestore,script update: 414,
    0: Fresh, Updated firstrun: 1,
    0: Fresh, Updated hash: 22,
    0: Fresh, Updated nothing: 16015,
    0: Fresh, Updated nothing,error: 19,
    0: Fresh, Updated script update: 165,
    1: Due, Updated filestore: 2,
    1: Due, Updated nothing: 259,
    1: Due, Updated nothing,error: 27,
    2: Overdue, Updated nothing: 26,
    2: Overdue, Updated nothing,error: 1,
    3: Delinquent, Updated nothing: 5166,
    3: Delinquent, Updated nothing,error: 24,
    Freshness Unavailable, Updated no resources: 10

1248 datasets have update frequency of Live
3978 datasets have update frequency of Never
2163 datasets have update frequency of As Needed

For more detailed analysis, the database it builds can can be queried eg.

select count(*) from dbresources where url like '%ourairports%' and dataset_id in (select id from dbdatasets where fresh is null); select count(*) from dbresources where url like '%ourairports%' and dataset_id in (select id from dbdatasets where update_frequency is null);



The above lines returned the same value (but may not now as the datasets had their update frequency changed), confirming to us that for 48 resources which have a url containing "ourairports", their freshness value is not calculable because the update frequency of the dataset is not set. This is only possible for datasets created prior to the HDX release which made the update frequency (renamed expected update frequency) mandatory.