kip

kip

ex_cldr Core Team

Tz_world / timezone_boundary_builder update

tz_world is a library that maps a location (lat, lng) to a timezone.

From time-to-time the base data in timzone_boundary_builder is updated based upon updates to the IANA timezone database.

With the IANA timezone database being updated to version 2020a on April 23rd, timezone_boundary_builder has now also updated its release.

For current users of tz_world you can update the data source in two ways:

  1. Run mix tz_world.update
  2. In a running system, call TzWorld.reload_timezone_data/0. Application restart is not required - the new data is downloaded and installed in the running system.

First Post!

LostKobrakai

LostKobrakai

Yeah, I can finally replace my manually imported world map in my db without update logic :slight_smile:

Most Liked

kip

kip

ex_cldr Core Team

I have refactored tz_world to separate different backend strategies. Please feel free to give it a try, I plan to release a new version on hex later this week.

New Backend Access modules

  • TzWorld.Backend.Memory which retains all data in memory for fastest performance at the expense of using approximately 1Gb of memory
  • TzWorld.Backend.Dets which uses Erlang’s :dets data store. This uses negligible memory at the expense of slow access times (approximaltey 500ms in testing)
  • TzWorld.Backend.DetsWithIndexCache which balances memory usage and performance. This backend is recommended in most situations since its performance is similar to TzWorld.Backend.Memory (about 5% slower in testing) and uses about 25Mb of memory
  • TzWorld.Backend.Ets which uses :ets for storage. With the default settings of :compressed for the :ets table its memory consumption is about 512Mb but with access that is over 20 times slower than TzWorld.Backend.DetsWithIndexCache

Most interesting is that by caching the bounding boxes in memory, the backend TzWorld.Backend.DetsWithIndexCache looks to be a good balance of memory utilisation and performance. It uses only ~20Mb of memory to store the bounding boxes and has performance similar to the in-memory backend.

Basic Benchmark

Name                                 ips        average  deviation         median         99th %
Backend Memory                     52.34       19.10 ms    ±14.86%       18.34 ms       30.62 ms
Backend DetsWithIndexCache         50.10       19.96 ms    ±13.15%       20.79 ms       24.66 ms
Backend Ets                         2.33      428.53 ms     ±2.51%      432.73 ms      441.58 ms
Backend Dets                        0.59     1693.80 ms     ±9.62%     1646.47 ms     2018.27 ms

Comparison: 
Backend Memory                     52.34
Backend DetsWithIndexCache         50.10 - 1.04x slower +0.85 ms
Backend Ets                         2.33 - 22.43x slower +409.43 ms
Backend Dets                        0.59 - 88.66x slower +1674.70 ms
kip

kip

ex_cldr Core Team

It’s been a year since any functional updates or bug fixes for tz_world so today I published tz_world version 1.0.0.

Really, the only change is the version number. Still actively maintained, just no open issues or functional enhancement requests.

kip

kip

ex_cldr Core Team

Yep, turns out its really big (and this is the version not including geo data for the oceans).

I will, for the next release:

  1. Turn the storage and access mechanisms into a behaviour
  2. Implement a :dets backend (first priority)
  3. Implement a PostGis backend (which was already written by the original author of tz_world)
  4. Implement a non-PostGis backend for filtering by bounding box

Probably going to take a couple of weeks to get this done.

Thanks for the vigilance @LostKobrakai.

Last Post!

kip

kip

ex_cldr Core Team

I’ve just published tz_world version 2.1.0.

The primary changes are:

  • The addition of a new backend, TzWorld.Backend.SpatialIndex that implements a pure-Elixir R-tree (Sort-Tile-Recursive packed) index. This index results in substantially faster lookups.

  • File and :json streaming to optimise the memory usage when running mix tz_world.update. This task used to consume a peak of 980Mb. It now consumes a peak of 70Mb.

The reason for the major release bump are:

  • The removal of two backends that just aren’t good choices: TzWorld.Backend.Memory and TzWorld.Backend.Ets; and

  • The new format of the on-disk data resulting from the new streaming build.

Performance comparisons

Benchmarked against the previous backends across a categorised fixture set (dense regions, sparse/large zones, small/thin zones, ocean points with no match, points adjacent to the international date line):

Input category Speedup vs. the next-fastest existing backend
ocean (no-match) 18.2×
sparse_or_large 1.64×
dense (cities) 1.43×
random uniform 1.42×
small_or_thin 1.08×

The largest wins are on no-match queries: previously, points in the open ocean caused a full scan of every shape’s bounding box; the R-tree exits at the root.

Memory comparison of mix tz_world.update

Version 2.0 rewrote the data-update pipeline to stream end-to-end. The source zip is downloaded straight to a temp file (no in-memory body), unzipped to disk (no in-memory JSON), parsed in 64 KiB chunks via OTP’s built-in :json module with a feature-diverting decoder callback, and each Geo.Polygon / Geo.MultiPolygon is written to the on-disk index as it is decoded. The full GeoJSON is never resident in memory at any point.

Measured BEAM peak memory of mix tz_world.update on the without-oceans dataset (158 MB GeoJSON, 419 shapes, post-GC sampled):

Version Peak BEAM memory during update
1.x ≈ 920 MB
2.x ≈ 70 MB

Where Next?

Popular in Announcing Top

Qqwy
Hello everyone, I wrote a small library today called MapDiff. It returns a map listing the (smallest amount of) changes to get from map...
New
kelvinst
Hey everyone! Well, we made this lib a while ago and now we decided to finally go out and public with it! It’s a tool for creating and m...
New
josevalim
EDIT: since Ecto 3.0 final version is out, this post was amended to use the final versions in the instructions below. Hi everyone, We a...
New
tmbb
I’ve been working on two packages (not on hex.pm yet) to build admin interfaces for phoenix apps: bureaucrat - which contains a bunch ...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 44778 311
New
benlime
LiveMotion enables high performance animations declared on the server and run on the client. As a follow up to my previous thread A libr...
New
markmark206
simple_feature_flags is a tiny package that lets you turn features on or off based on which environment (e.g. localhost, staging, product...
New

Other popular topics Top

nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 55125 245
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New

We're in Beta

About us Mission Statement