kip
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:
- Run
mix tz_world.update - 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
Most Liked
kip
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.Memorywhich retains all data in memory for fastest performance at the expense of using approximately 1Gb of memoryTzWorld.Backend.Detswhich uses Erlang’s:detsdata store. This uses negligible memory at the expense of slow access times (approximaltey 500ms in testing)TzWorld.Backend.DetsWithIndexCachewhich balances memory usage and performance. This backend is recommended in most situations since its performance is similar toTzWorld.Backend.Memory(about 5% slower in testing) and uses about 25Mb of memoryTzWorld.Backend.Etswhich uses:etsfor storage. With the default settings of:compressedfor the:etstable its memory consumption is about 512Mb but with access that is over 20 times slower thanTzWorld.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
Yep, turns out its really big (and this is the version not including geo data for the oceans).
I will, for the next release:
- Turn the storage and access mechanisms into a
behaviour - Implement a
:detsbackend (first priority) - Implement a
PostGisbackend (which was already written by the original author of tz_world) - 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
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
:jsonstreaming to optimise the memory usage when runningmix 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.MemoryandTzWorld.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 |
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









