<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>danigm.net - fractal</title><link>https://danigm.net/</link><description></description><lastBuildDate>Tue, 30 Jun 2020 00:00:00 +0200</lastBuildDate><item><title>Fractal: Refactoring and the review process</title><link>https://danigm.net/gsoc-2020-1.html</link><description>&lt;p&gt;In this year GSoC, &lt;a href="https://aledomu.github.io/gnome/refactoring-fractal-remove-backend-i/"&gt;Alejandro&lt;/a&gt; is working on Fractal, moving code from the
backend to the client, to try to simplify the code used to communicate with the
matrix.org server and maybe in the future we can replace &lt;code&gt;fractal-matrix-api&lt;/code&gt;
with the &lt;a href="https://matrix.org/sdks/#matrix-rust-sdk"&gt;matrix-rust-sdk&lt;/a&gt;. And then we'll have less code in our project to
maintain.&lt;/p&gt;
&lt;p&gt;This is a great work, something needed in a project with a technological debt
of several years. I created this project to learn Rust, and also I was learning
about the matrix protocol during the project build. And other contributors do
the same. So we've been building one thing on top another for a lot of years.&lt;/p&gt;
&lt;p&gt;In this kind of community driven projects it's the way to go. For some time
we've people interested and developers think about the design and start change
some parts or to write new functionality following a new design pattern. But
voluntary developers motivation change in time and they left the project and
the next one continues the work with a different vision.&lt;/p&gt;
&lt;p&gt;It's not something bad, it's the greatness of the open source. Different people
has different motivations to participate in a free software project, and every
contribution is welcome. I'm the maintainer of the project and I've spent a lot
of time building Fractal, but I don't have the same motivation now to work on
the project, to it's good to have other people working on it so it can continue
alive.&lt;/p&gt;
&lt;p&gt;Alejandro is doing a great work and he's not a 4 months contributor. He's
working on the backend refactoring for 2 years now, step by step and he has
plans for the future.&lt;/p&gt;
&lt;p&gt;Refactoring a big project is always hard, because there's a lot of code
movement and always there's the fear to regressions.&lt;/p&gt;
&lt;p&gt;Rust is a great language and it shines when big code refactoring comes to the
scene. If it compiles, you &lt;strong&gt;know&lt;/strong&gt; that there's no memory errors, dangling
pointers and that kind of problems. If it builds it will work.&lt;/p&gt;
&lt;p&gt;But maybe it will work &lt;em&gt;different&lt;/em&gt;, so the review process is needed to ensure
that the application continues working.&lt;/p&gt;
&lt;p&gt;Automated tests are really useful for big code changes and project refactoring,
because you have a quick picture and some certainty that the project is
working. But we don't have tests in fractal :D, so someone should do that.&lt;/p&gt;
&lt;p&gt;So here I am. &lt;a href="https://gitlab.gnome.org/GNOME/fractal/-/merge_requests/581"&gt;Reviewing large MR&lt;/a&gt;, with a lot of lines. At least gitlab
makes this process a bit easier.&lt;/p&gt;
&lt;p&gt;What I'm trying to do in the review process is to just read the whole diff and
check if there's some problem in the code. And after every change, I run the
app and I do some tests, trying to use the functionality that could be broken
by those new changes.&lt;/p&gt;
&lt;p&gt;This takes a lot of time and it's not something fun to do... But someone has to
do that. And during that process, sometimes I learn something new. Reading code
is an interesting task and try to find bugs in code while reading it, is
something useful. To think about that code, what it does and why.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Tue, 30 Jun 2020 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2020-06-30:/gsoc-2020-1.html</guid><category>blog</category><category>gnome</category><category>software</category><category>fractal</category><category>gsoc</category></item><item><title>Fractal: Google Summer of Code 2020</title><link>https://danigm.net/gsoc-2020.html</link><description>&lt;p&gt;I'm glad to say that I'll participate again in the &lt;a href="https://summerofcode.withgoogle.com/"&gt;GSoC&lt;/a&gt;, as mentor. This
summer we'll try to implement multi-account support in &lt;a href="https://gitlab.gnome.org/GNOME/fractal"&gt;Fractal&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/fractal-gsoc.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The selected student is &lt;a href="https://summerofcode.withgoogle.com/projects/#6726209787920384"&gt;Alejandro Dominguez&lt;/a&gt; (aledomu), that is
collaborating with Fractal since the Seville Hackfest in December 2018, doing
a great work in the backend. Alejandro is young developer with a lot of energy
and ideas, so it's great to have this kind of people working on GNOME.&lt;/p&gt;
&lt;p&gt;I've not spend much time lately developing Fractal, the time and energy is
limited, but I'll try to use this GSoC mentorship to go back to the &lt;strong&gt;active&lt;/strong&gt;
Fractal development. My objective during this summer will be to stabilize,
fix bugs and improve the performance and create a new release at the end of the
GSoC, because we've a lot of new functionality in master, but I didn't spend
the time to do the release.&lt;/p&gt;
&lt;p&gt;The google summer of code is a great opportunity for free software, it give
us a full time working student, during three months, working on free software,
so it's really appreciated in a community where a lot of work is volunteer work.&lt;/p&gt;
&lt;p&gt;There are a total of 14 projects selected for this GSoC round for gnome, you
can take a look to the full list in the &lt;a href="https://summerofcode.withgoogle.com/organizations/5428225724907520/#projects"&gt;GSoC page&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 08 May 2020 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2020-05-08:/gsoc-2020.html</guid><category>blog</category><category>gnome</category><category>software</category><category>fractal</category><category>gsoc</category></item><item><title>GNOME Outreachy mentorship</title><link>https://danigm.net/gnome-outreachy-2018.html</link><description>&lt;p&gt;The &lt;a href="https://www.outreachy.org/"&gt;Outreachy&lt;/a&gt; program is a three month internship to work in FOSS. There
are two periods for the outreachy, the first one from December to March and
the other one from May to August. It's similar to the Google Summer Of Code,
but in this case the interns doesn't need to be students.&lt;/p&gt;
&lt;p&gt;I proposed some ideas for interts to work on GNOME, with me as a mentor. I
wrote three proposals this time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extend &lt;a href="https://www.outreachy.org/communities/cfp/gnome/project/extend-fractal-media-viewer-with-video-support-and/"&gt;Fractal&lt;/a&gt; media viewer with video support and explore video
   conference&lt;/li&gt;
&lt;li&gt;Create &lt;a href="https://www.outreachy.org/communities/cfp/gnome/project/create-gtranslator-initial-integration-with-damned/"&gt;Gtranslator&lt;/a&gt; initial integration with Damned Lies&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.outreachy.org/communities/cfp/gnome/project/books-improve-the-epub-support-in-gnome-books/"&gt;Books&lt;/a&gt; : Improve the epub support in gnome-books&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There was people interested in all of three projects, but for the Books app
we don't have any real contribution so there was no real applicants.&lt;/p&gt;
&lt;p&gt;I've two good proposals for Fractal and for Gtranslator, so I approve both and
the Outreachy people approve these two interts. So we get two new devs working
in GNOME for three months as interns.&lt;/p&gt;
&lt;p&gt;This is something great, paid developers working in my proposals is a good
thing, but this implies that I need to do the mentor work for these two interns
during the three months period, so it's more work for me :/&lt;/p&gt;
&lt;p&gt;But I think this is a really important work to do to bring more people to the
free software, so I've less time for hacking, but I think it's good, because
the fresh blood can do the hacking and if, after the Outreachy, one of the
interns  continues collaborating with GNOME, that will be more important for
the GNOME project that some new features in one app.&lt;/p&gt;
&lt;h3&gt;GNOME Translation Editor&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://teja.cetinski.eu/blog"&gt;Teja&lt;/a&gt; is the intern working in gtranslator. She is working in the
&lt;a href="https://l10n.gnome.org/"&gt;Damned Lies&lt;/a&gt; integration.&lt;/p&gt;
&lt;p&gt;Damned Lies is a web application for GNOME translators. This app provides
updated &lt;code&gt;.po&lt;/code&gt; file for each GNOME module and language and translators can
download and update that file using the web interface. That web is able to
do the commit to the original repository with the upload version from
translators.&lt;/p&gt;
&lt;p&gt;The idea is to provide a simple integration with this platform in the
GNOME Translation Editor app, using the web json API to be able to open &lt;code&gt;.po&lt;/code&gt;
files from the web directly without the need to download the file and then
open it.&lt;/p&gt;
&lt;p&gt;The current API of DL is really simple so we can't implement a real integration
without adding more functionality to this API. So this project requires some
work in the DL app too.&lt;/p&gt;
&lt;p&gt;In the future we can improve the integration adding the posibility to upload
the new &lt;code&gt;.po&lt;/code&gt; after saving to DL so translators doesn't need to go to the
web interface and can do all the translation flow using only the Translation
Editor.&lt;/p&gt;
&lt;h3&gt;Fractal&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://mairandom.space/"&gt;Maira&lt;/a&gt; is the intern working in Fractal. They are working in the initial
video preview widget.&lt;/p&gt;
&lt;p&gt;Fractal is a instant messaging app that works over matrix.org. Currently we
support different types of messages, like text, images, audio and files. But
for video we're using the same widget as we're using for files, so you can
download or open, but we've not a preview or inline player.&lt;/p&gt;
&lt;p&gt;The main idea of this project is to provide a simple video player using
gstreamer to play the video inside the Fractal app.&lt;/p&gt;
&lt;p&gt;This is not an easy task, because we're using Rust in Fractal and we need to
deal with bindings and language stuff, but I think it's doable.&lt;/p&gt;
&lt;p&gt;During the internship, Maira is also working fixing some bugs in the audio
player, becuase it uses gstreamer too, so during the code review, Maira
detected some problems and they are fixing it.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sun, 13 Jan 2019 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2019-01-13:/gnome-outreachy-2018.html</guid><category>blog</category><category>gnome</category><category>fractal</category><category>gtranslator</category><category>outreachy</category></item><item><title>Fractal December'18 Hackfest (part 2)</title><link>https://danigm.net/fractal-december-18-hackfest-2.html</link><description>&lt;p&gt;The Friday 14th was the last day of the &lt;a href="https://wiki.gnome.org/Hackfests/FractalDecember2018"&gt;second Fractal Hackfest&lt;/a&gt;. I've not
spend much time writing real code, the Thursday was mainly another hacking day
and I've been able to continue with the &lt;strong&gt;fractal-backend&lt;/strong&gt; creation, but
there's a lot of work to do there.&lt;/p&gt;
&lt;p&gt;But the hackfest was really productive, we've talked about big issues, project
management, some design ideas, new functionalities, the application refactor,
etc.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-seville-hackfest-2.jpg" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h3&gt;GNOME newcomers experience&lt;/h3&gt;
&lt;p&gt;We talked about how to improve the GNOME newcomers experience and how to
improve the main view of Fractal. I think that Tobias will talk more about this,
he was working in some cool design for this and I think we can start to
implement this new views soon.&lt;/p&gt;
&lt;h3&gt;Best practices&lt;/h3&gt;
&lt;p&gt;We've been developing Fractal in a fast way, without spend a lot of time
thinking about the code quality, maintainability and that stuff. Recently we've
set the &lt;strong&gt;rustfmt&lt;/strong&gt; linter in the CI pipeline and we've some tests, but the
Merge Requests process wasn't defined and for example I was pushing directly
to master.&lt;/p&gt;
&lt;p&gt;To improve the quality of Fractal we've started a new wiki page to have a list
of &lt;a href="https://gitlab.gnome.org/GNOME/fractal/wikis/Best-practices-for-Fractal-development"&gt;best practices&lt;/a&gt; to follow. There we'll add some guidances on how we
should write code and the processes to follow to improve Fractal.&lt;/p&gt;
&lt;p&gt;We've decided that we should be more strict with the Merge Request code review
and now, direct push to master is not allowed, all changes will go through the
review process. We should wait at least two days to merge something and have
at least two people that approve the change.&lt;/p&gt;
&lt;p&gt;This will slow down the MR process, but will improve the code quality and
will reduce regressions. Any help is welcome, if you're able to test the MR,
you can leave a comment and other reviewers will have more confident in the
change.&lt;/p&gt;
&lt;p&gt;We're also working in the code quality using the cargo clippy tool. There's a 
Merge Request waiting for review, so we'll have a better rust source code soon.&lt;/p&gt;
&lt;h3&gt;Fractal is now in the GNOME group&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://gitlab.gnome.org/GNOME/fractal/"&gt;Fractal&lt;/a&gt; project was on the &lt;strong&gt;World&lt;/strong&gt; group inside the GNOME gitlab.
Fractal is a GNOME application, the most active developers are GNOME developers
and we try to follow the GNOME Human Interface Guidelines.&lt;/p&gt;
&lt;p&gt;Fractal is one of the first new applications that born just during the gitlab
migration so we go through a new process. At first Fractal was in my personal
gitlab under &lt;strong&gt;/danigm/fractal&lt;/strong&gt; then we move to the World group under
&lt;strong&gt;/World/fractal&lt;/strong&gt; and finally we're in the main GNOME group &lt;strong&gt;/GNOME/fractal&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;New release 4.0.0&lt;/h3&gt;
&lt;p&gt;The last release was the 3.30.0, more than three months has passed since this
release and we've a lot of changes so we want to provide a new stable release.&lt;/p&gt;
&lt;p&gt;We discuss a bit about the &lt;a href="https://gitlab.gnome.org/GNOME/fractal/issues/350"&gt;version number&lt;/a&gt; that we should follow and we
decided that we should do use our own version number system, because we want
to release as much as possible, when we've important changes.&lt;/p&gt;
&lt;p&gt;We're working in the &lt;a href="https://gitlab.gnome.org/GNOME/fractal/issues/396"&gt;4.0.0&lt;/a&gt; release, we're stabilizing and fixing important
bugs before the release and maybe we can have the new release during the next
week.&lt;/p&gt;
&lt;p&gt;So I've spend the last day looking for bugs and preparing the new release.&lt;/p&gt;
&lt;h3&gt;Matrix Live&lt;/h3&gt;
&lt;p&gt;We've a meeting with the people from Matrix.org to talk about Fractal and the
hackfest. You can view the full interview in youtube:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/SgyLHi8zZXQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h2&gt;Friday sponsored lunch&lt;/h2&gt;
&lt;p&gt;We've a sponsored lunch the Friday 14th, the local group &lt;a href="https://www.plan4d.eu"&gt;Plan4D&lt;/a&gt; invite
us to a great lunch in the city center.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.plan4d.eu/"&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/plan4d.jpg"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After that, I goes back to my home and leave the other people there in Seville
doing some tourism, I've to go back to Málaga.&lt;/p&gt;
&lt;p&gt;The hackfest was great and we have done a lot of things. This was the second
Fractal hackfest in 2018, and we meet at the GUADEC too. We've had two GSoC
students and now we've another intern thanks to the outreachy program. There's
a lot of people contributing to Fractal and that's great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GNOME is a great community&lt;/strong&gt; and I think that Matrix.org and Rust are helping
to this project a lot. The Matrix.org people are supporting us, indeed, Matthew
comes to the first hackfest and I think that the success of Fractal and the
community behind has a lot to thank to the Rust language and to the people
working in the Rust + GNOME integration, Gtk-rs is a great project.&lt;/p&gt;
&lt;p&gt;I want to apologize about the network problem during the hackfest. We've been
working all days thank to Julian network sharing, with eduroam, because we
aren't able to have guest access in the university.&lt;/p&gt;
&lt;p&gt;The university has a strict internet connection filtering, so only a professor
can ask for a guest connection for events and we do the request too late.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sun, 16 Dec 2018 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2018-12-16:/fractal-december-18-hackfest-2.html</guid><category>blog</category><category>gnome</category><category>fractal</category><category>hackfest</category><category>wadobo</category><category>seville</category></item><item><title>Fractal December'18 Hackfest (part 1)</title><link>https://danigm.net/fractal-december-18-hackfest-1.html</link><description>&lt;h2&gt;First two days of work&lt;/h2&gt;
&lt;p&gt;The Tuesday 11th started the &lt;a href="https://wiki.gnome.org/Hackfests/FractalDecember2018"&gt;second Fractal Hackfest&lt;/a&gt;. I've organized this
hackfest in Seville, the city where I studied computer science and here I've a
lot of friends in the University so is a good place to do it here.&lt;/p&gt;
&lt;p&gt;The weather was important too for the hackfest selection, in December
Seville is a good choice because the weather is not too cold, we're having
sunny days.&lt;/p&gt;
&lt;p&gt;The first day was a good day, thinking about some relevant issues and planning
what we want to do. We talked about the work needed for the interface split,
about the E2EE support, new features and the need for a new release.&lt;/p&gt;
&lt;p&gt;We're having some problems with the internet connection, because the University
has a restricted network policy and we ask for the guess internet connection
the Monday, but we're still waiting.&lt;/p&gt;
&lt;p&gt;Meantime we have to thanks to Julian Sparber the hackfest wifi, because
he's using his laptop to stream the eduroam connection.&lt;/p&gt;
&lt;h2&gt;Newcomers&lt;/h2&gt;
&lt;p&gt;The first day we try to promote as a newcomers day, and some people comes.
We've some contributions and I spend some time trying to help people to
introduce to the GNOME community.&lt;/p&gt;
&lt;h2&gt;GNOME Foundation sponsored dinner&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://gnome.org"&gt;GNOME Foundation&lt;/a&gt; payed for a Fractal dev day dinner, so we should
thanks this great dinner to GNOME:&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-seville-hackfest-dinner.jpg" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;We've a good time eating and drinking in the &lt;em&gt;Coco Verde&lt;/em&gt;. We talk about GNOME,
the desktop, matrix.org and other communication tools.&lt;/p&gt;
&lt;h1&gt;Hacking day&lt;/h1&gt;
&lt;p&gt;Today was a hacking day, so we started to work in the stuff that we talk about
yesterday. We've resolved some minor issues and I started with the
&lt;strong&gt;fractal-backend&lt;/strong&gt; crate.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-seville-hackfest-computer.jpg" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;We talked about move all the app data model and logic to the &lt;strong&gt;fractal-backend&lt;/strong&gt;
and leave &lt;strong&gt;fractal-gtk&lt;/strong&gt; only as UI management.&lt;/p&gt;
&lt;p&gt;We talked about the &lt;strong&gt;LMDB&lt;/strong&gt; use to store rooms and messages and we decided
that the best solution should be to use a relational database, because we've
relations and with the key-value thing we'll end creating those relations and
maintaining by hand. In any case, I've this decision in mind and I'm
implementing all this with a trait to hide the storage detail so we can change
easily in the future.&lt;/p&gt;
&lt;p&gt;So today I've spend a lot of time implementing this trait and a first
implementation for the &lt;strong&gt;Room&lt;/strong&gt; struct. I've decided to use &lt;strong&gt;rusqlite&lt;/strong&gt; instead
the &lt;strong&gt;diesel&lt;/strong&gt; orm because I want to keep it simple.&lt;/p&gt;
&lt;p&gt;I want to finish all the database storage and move the AppOp main loop to the
&lt;strong&gt;fractal-backend&lt;/strong&gt; and try to update the &lt;strong&gt;fractal-gtk&lt;/strong&gt; to use the backend
instead the AppOp struct to get the rooms and messages. But maybe is a lot of
work, I don't know if I'll be able to finish this before the end of the
hackfest.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-seville-hackfest-julian.jpg" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;This hackfest is possible because there's a lot of volunteer work and people
helping us. First of all, the Fractal core team that comes &lt;em&gt;Tobias Bernard&lt;/em&gt;,
&lt;em&gt;Julian Sparber&lt;/em&gt; and &lt;em&gt;Alexandre Franke&lt;/em&gt; and of course the &lt;em&gt;GNOME Foundation&lt;/em&gt;.
And also we've to thank &lt;em&gt;Alejandro Domínguez&lt;/em&gt;, a newcomer that is doing a really
good job fixing bugs and cleaning some old code.&lt;/p&gt;
&lt;p&gt;Then I want to thank the &lt;a href="https://sugus.eii.us.es/"&gt;Linux local group, SUGUS&lt;/a&gt; for the help and also
I want to thank to other free software related group &lt;a href="https://www.plan4d.eu"&gt;Plan4D&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.plan4d.eu/"&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/plan4d.jpg"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Plan4D is giving us some help with the place and cookies, fruits, juices and
some tea.&lt;/p&gt;
&lt;p&gt;And finally I want to thank again to my coworkers in &lt;a href="https://wadobo.com"&gt;wadobo&lt;/a&gt; because they
support me to spend a full work week working in gnome and they also spend some
time helping us to organize all the hackfest stuff.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Wed, 12 Dec 2018 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2018-12-12:/fractal-december-18-hackfest-1.html</guid><category>blog</category><category>gnome</category><category>fractal</category><category>hackfest</category><category>wadobo</category><category>seville</category></item><item><title>mdl</title><link>https://danigm.net/mdl.html</link><description>&lt;p&gt;The last month I wrote a blog post about the &lt;a href="http://danigm.net/lmdb.html"&gt;LMDB Cache database&lt;/a&gt; and my
wish to use that in Fractal. To summarize, LMDB is a memory-mapped key-value
database that persist the data to the filesystem. I want to use this in the
Fractal desktop application to replace the current state storage system
(we're using simple json files) and as a side effect we can use this storage
system to share data between threads because currently we're using a big
struct &lt;code&gt;AppOp&lt;/code&gt; shared with &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;AppOp&amp;gt;&amp;gt;&lt;/code&gt; and this cause some problems
because we need to share and lock and update the state there.&lt;/p&gt;
&lt;p&gt;The main goal is to define an app data model with smaller structs and store
this using LMDB, then we can access to the same data querying the LMDB and we
can update the app state storing to the LMDB.&lt;/p&gt;
&lt;p&gt;With this change we don't need to share these structs, we only need to query
to the LMDB to get the data and the work with that, and this should simplify
our code. The other main benefit will be that we'll have this state in the
filesystem by default so when we open the app after close, we'll stay in the
same state.&lt;/p&gt;
&lt;p&gt;Take a look to the &lt;a href="https://gitlab.gnome.org/danigm/mdl/blob/master/examples/gtkapp/src/main.rs"&gt;gtk TODO example app&lt;/a&gt; to view how to use &lt;em&gt;mdl&lt;/em&gt; with
signals in a real gtk app.&lt;/p&gt;
&lt;h2&gt;What is mdl&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://crates.io/crates/mdl"&gt;mdl&lt;/a&gt; is Data model library to share app state between threads and process
and persist the data in the filesystem. Implements a simple way to store
structs instances in a LMDB database and other methods like BTreeMap.&lt;/p&gt;
&lt;p&gt;I started to play with the LMDB rust binding and writing some simple tests.
After some simple tests, I decided to write a simple abstraction to hide the
LMDB internals and to provide a simple data storage and to do that I created
the &lt;strong&gt;mdl&lt;/strong&gt; crate.&lt;/p&gt;
&lt;p&gt;The idea is to be able to define your app model as simple rust structs. LMDB is
a key-value database so every struct instance will have an unique &lt;em&gt;key&lt;/em&gt; to
store in the cache.&lt;/p&gt;
&lt;p&gt;The keys are stored in the cache ordered, so we can use some techniques to
store related objects and to retrieve all objects of a kind, we only need to
build keys correctly, following an scheme. For example, for fractal we can
store rooms, members and messages like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rooms with key "room:roomid", to store all the room information, title,
   topic, icon, unread msgs, etc.&lt;/li&gt;
&lt;li&gt;members with key "member:roomid:userid", to store all member information.&lt;/li&gt;
&lt;li&gt;messages with key "msg:roomid:msgid" to store room messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Following this key assignment we can iterate over all rooms by querying all
objects that starts with "room", we can get all members and all messages from
a room.&lt;/p&gt;
&lt;p&gt;This have some inconveniences, because we can't query directly an message by
id if we don't know the roomid. If we need that kind of queries, we need to
think about another key assignment or maybe we should duplicate data. key-value
are simple databases so we don't have the power of relational databases.&lt;/p&gt;
&lt;h2&gt;Internals&lt;/h2&gt;
&lt;p&gt;LMDB is fast and efficient, because it's in memory so using this cache won't
add a lot of overhead, but to make it simple to use I've to add some overhead,
so mdl is easy by default and can be tuned to be really fast.&lt;/p&gt;
&lt;p&gt;This crate has three main modules with traits to implement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;model&lt;/strong&gt;: This contains the &lt;code&gt;Model&lt;/code&gt; trait that should implement every
   struct that we want to make cacheable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;store&lt;/strong&gt;: This contains the &lt;code&gt;Store&lt;/code&gt; trait that's implemented by all the
   cache systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;signal&lt;/strong&gt;: This contains the &lt;code&gt;Signaler&lt;/code&gt; trait and two structs that allow
   us to emit/subscribe to "key" signals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And two more modules that implements the current two cache systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;cache&lt;/strong&gt;: LMDB cache that implements the &lt;code&gt;Store&lt;/code&gt; trait.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;bcache&lt;/strong&gt;: BTreeMap cache that implements the &lt;code&gt;Store&lt;/code&gt; trait. This is a good
   example of other cache system that can be used, this doesn't persist to the
   filesystem.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we've two main concepts here, the &lt;strong&gt;Store&lt;/strong&gt; and the &lt;strong&gt;Model&lt;/strong&gt;. The model
is the plain data and the store is the container of data. We'll be able to add
models to the store or to query the store to get stored models. We store our
models as key-value where the key is a &lt;code&gt;String&lt;/code&gt; and the value is a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;,
so every model should be serializable.&lt;/p&gt;
&lt;p&gt;This serialization is the bigger overhead added. We need to do this because
we need to be able to store this in the LMDB database. Every request will create
a copy of the object in the database, so we're not using the same data. This can
be tuned to use pointers to the real data, but to do that we'll need to use
&lt;em&gt;unsafe&lt;/em&gt; code and I think that the performance that we'll get with this doesn't
deserve the complexity that this will add.&lt;/p&gt;
&lt;p&gt;By default, the &lt;code&gt;Model&lt;/code&gt; trait has two methods &lt;code&gt;fromb&lt;/code&gt; and &lt;code&gt;tob&lt;/code&gt; to serialize
and deserialize using &lt;a href="https://crates.io/crates/bincode"&gt;bincode&lt;/a&gt;, so any struct that implements the &lt;code&gt;Model&lt;/code&gt;
trait and doesn't reimplement these two methods should implement &lt;code&gt;Serialize&lt;/code&gt;
and &lt;code&gt;Deserialize&lt;/code&gt; from &lt;a href="https://crates.io/crates/serde_derive"&gt;serde&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The signal system is an addition to be able to register callbacks to &lt;strong&gt;keys&lt;/strong&gt;
modifications in the &lt;strong&gt;store&lt;/strong&gt;, so we can do something when a new objects is
added, modified or deleted from the store. The signaler is optional and we
should use it in a explicit way.&lt;/p&gt;
&lt;h2&gt;How to use it&lt;/h2&gt;
&lt;p&gt;First of all, you should define your data model, the struct that you want to
be able to store in the database:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#[derive(Serialize, Deserialize, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example we'll define a struct called &lt;strong&gt;A&lt;/strong&gt; with two attributes, &lt;strong&gt;p1&lt;/strong&gt;,
a &lt;code&gt;String&lt;/code&gt;, and &lt;strong&gt;p2&lt;/strong&gt;, an &lt;code&gt;u32&lt;/code&gt;. We derive &lt;code&gt;Serialize&lt;/code&gt; and &lt;code&gt;Deserialize&lt;/code&gt;
because we're using the default &lt;code&gt;fromb&lt;/code&gt; and &lt;code&gt;tob&lt;/code&gt; from the &lt;code&gt;Model&lt;/code&gt; trait.&lt;/p&gt;
&lt;p&gt;Then we need to implement the &lt;code&gt;Model&lt;/code&gt; trait:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}:{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We only reimplement the &lt;code&gt;key&lt;/code&gt; method to build a key for every instance of &lt;code&gt;A&lt;/code&gt;.
In this case our key will be the &lt;code&gt;String&lt;/code&gt; followed by the number, so for example
if we've something like &lt;code&gt;let a = A { p1: "myk", p2: 42 };&lt;/code&gt; the key will be
"myk:42".&lt;/p&gt;
&lt;p&gt;Then, to use this we need to have a &lt;code&gt;Store&lt;/code&gt;, in this example, we'll use the
LMDB store that's the struct &lt;code&gt;Cache&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// initializing the cache. This str will be the fs persistence path&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/tmp/mydb.lmdb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We pass the path to the filesystem where we want to persist the cache as the
first argument, in this example we'll persist to "/tmp/mydb.lmdb". When we
ran the program for the first time a directory will be created there. The next
time, that cache will be used with the information from the previous execution.&lt;/p&gt;
&lt;p&gt;Then, with this &lt;code&gt;cache&lt;/code&gt; object we can instantiate an &lt;code&gt;A&lt;/code&gt; object and store
in the &lt;code&gt;cache&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// create a new *object* and storing in the cache&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;store&lt;/code&gt; method will serialize the object and store a copy of that in the
cache.&lt;/p&gt;
&lt;p&gt;After the store, we can query for this object from other process, using the
same lmdb path, or from the same process using the cache:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// querying the cache by key and getting a new *instance*&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello:42&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll get a copy of the original one.&lt;/p&gt;
&lt;p&gt;This is the full example:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#[macro_use]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;serde_derive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Serialize, Deserialize, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{}:{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// initializing the cache. This str will be the fs persistence path&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/tmp/mydb.lmdb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// create a new *object* and storing in the cache&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// querying the cache by key and getting a new *instance*&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello:42&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Iterations&lt;/h3&gt;
&lt;p&gt;When we store objects with the same key prefix we can iterate over all of
the objects, because we don't know the full key of all objects.&lt;/p&gt;
&lt;p&gt;Currently there's two ways to iterate over all objects with the same prefix
in a &lt;code&gt;Store&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;all&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the simpler way, calling the &lt;code&gt;all&lt;/code&gt; method we'll receive a &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt; so
we've all the objects in a vector.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hellows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hellows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hellow: {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This has a little problem, because if we've a lot of objects, this will use a
lot of memory for the vector and we'll be iterating over all objects twice. To
solve this problems, the &lt;code&gt;iter&lt;/code&gt; method was created.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;iter&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;iter&lt;/code&gt; method provides a way to call a closure for every object with this
prefix in the key. This closure should return a &lt;code&gt;Continue(bool)&lt;/code&gt; that will
indicates if we should continue iterating of if we should stop the iteration
here.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hellow: {}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Continue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using the &lt;code&gt;Continue&lt;/code&gt; we can avoid to iterate over all the objects, for example
if we're searching for one concrete object.&lt;/p&gt;
&lt;p&gt;We're copying every object, but the &lt;code&gt;iter&lt;/code&gt; method is better than the &lt;code&gt;all&lt;/code&gt;,
because if we don't copy or move the object from the closure, this copy only
live in the closure scope, so we'll use less memory and also, we only iterate
one. If we use &lt;code&gt;all&lt;/code&gt;, we'll iterate over all objects with that prefix to build
the vector so if we iterate over that vector another time this will cost more
than the &lt;code&gt;iter&lt;/code&gt; version.&lt;/p&gt;
&lt;h2&gt;Signal system&lt;/h2&gt;
&lt;p&gt;As I said before, the signal system provide us a way to register callbacks
to key modifications. The signal system is independent of the &lt;code&gt;Model&lt;/code&gt; and
&lt;code&gt;Store&lt;/code&gt; and can be used independently:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Signaler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SignalerAsync&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mdl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SignalerAsync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal_loop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// one thread for receive signals&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JoinHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_sig&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// waiting for threads to finish&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// one thread for emit signals&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JoinHandle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal:2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal:2:3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// waiting for threads to finish&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ten_millis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;from_millis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ten_millis&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example we're creating a &lt;code&gt;SignalerAsync&lt;/code&gt; that can &lt;code&gt;emit&lt;/code&gt; signal and
we can &lt;code&gt;subscribe&lt;/code&gt; a callback to any signal. The &lt;code&gt;sig.signal_loop();&lt;/code&gt; init the
signal loop thread, that wait for signals and call any subscribed callback when
a signal comes.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_sig&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We subscribe a callback to the signaler. The signaler can be cloned and the list
of callbacks will be the same, if you emit a signal in a clone and subscribe in
other clone, that signal will trigger the callback.&lt;/p&gt;
&lt;p&gt;Then we're emiting some signals:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal:2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;sig2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;signal:2:3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All of this three signals will trigger the previous callback because the
subscription works as a &lt;em&gt;signal starts with&lt;/em&gt;. This allow us to subscribe to
all new room messages insertion if we follow the previous described keys,
subscribing to "msg:roomid" and if we only want to register a callback to
be called only when one message is updated we can subscribe to
"msg:roomid:msgid" and this callback won't be triggered for other messages.&lt;/p&gt;
&lt;p&gt;The callback should be a &lt;code&gt;Box&amp;lt;Fn(signal)&amp;gt;&lt;/code&gt; where signal is the following
struct:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#[derive(Clone, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SigType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#[derive(Clone, Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Signal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SigType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Currently only &lt;code&gt;Update&lt;/code&gt; and &lt;code&gt;Delete&lt;/code&gt; signal types are supported.&lt;/p&gt;
&lt;h3&gt;Signaler in gtk main loop&lt;/h3&gt;
&lt;p&gt;All the UI operations in a gtk app should be executed in the gtk main loop so
we can't use the &lt;code&gt;SignalerAsync&lt;/code&gt; in a gtk app, because this signaler creates
one thread for the callbacks so all callbacks should implement the &lt;code&gt;Send&lt;/code&gt; trait
and if we want to modify, for example, a &lt;code&gt;gtk::Label&lt;/code&gt; in a callback, that
callback won't implement &lt;code&gt;Send&lt;/code&gt; because &lt;code&gt;gtk::Label&lt;/code&gt; can't be send between
threads safely.&lt;/p&gt;
&lt;p&gt;To solve this problem, I've added the &lt;code&gt;SignalerSync&lt;/code&gt;. That doesn't launch any
threads and where all operations runs in the same thread, even the callback.
This is a problem if one of your callbacks locks the thread, because this will
lock your interface in a gtk app, so any callback in the sync signaler should
be non blocking.&lt;/p&gt;
&lt;p&gt;This signaler should be used in a different way, so we should call from time
to time to the &lt;code&gt;signal_loop_sync&lt;/code&gt; method, that will check for new signals and
will trigger any subscribed callback. This signaler doesn't have a &lt;code&gt;signal_loop&lt;/code&gt;
because we should do the loop in our thread.&lt;/p&gt;
&lt;p&gt;This is an example of how to run the signaler loop inside a gtk app:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SignalerSync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;timeout_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;gtk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Continue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sig1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal_loop_sync&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// We can subscribe callbacks using the sig here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example code we're registering a timeout callback, every 50ms this
closure will be called, from the gtk main thread, and the &lt;code&gt;signal_loop_sync&lt;/code&gt;
will check for signals and call the needed callbacks.&lt;/p&gt;
&lt;p&gt;This method returns a &lt;code&gt;bool&lt;/code&gt; that's false when the signaler stops. You can
stop the signaler calling the &lt;code&gt;stop&lt;/code&gt; method.&lt;/p&gt;
&lt;h2&gt;Point of extension&lt;/h2&gt;
&lt;p&gt;I've tried to make this crate generic to be able to extend in the future and
provide other kind of cache that can be used changing little code in the apps
that uses &lt;strong&gt;mdl&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is the main reason to use traits to implement the store, so the first point
of extension is to add more cache systems, we're currently two, the LMDB and
the BTreeMap, but it would be easy to add more key-value storages, like
memcached, &lt;a href="https://crates.io/crates/unqlite"&gt;unqlie&lt;/a&gt;, mongodb, redis, couchdb, etc.&lt;/p&gt;
&lt;p&gt;The signaler is really simple, so maybe we can start to think about new
signalers that uses &lt;code&gt;Futures&lt;/code&gt; and other kind of callbacks registration.&lt;/p&gt;
&lt;p&gt;As I said before, mdl does a copy of the data on every write and on every read,
so it could be cool to explore the implication of these copies in the
performance and try to find methods to reduce this overhead.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 03 Aug 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-08-03:/mdl.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>mdl</category><category>fractal</category></item><item><title>LMDB: Cache database in memory</title><link>https://danigm.net/lmdb.html</link><description>&lt;p&gt;Some days ago I was in a meeting talking about the E2E implementation for
Fractal, to know the current implementation state and to talk with other
developers. This meeting was promoted by &lt;a href="https://puri.sm/"&gt;Puri.sm&lt;/a&gt; people, because they
want to use Fractal for the Librem5 phone and they want to have E2E. There's
people working in the E2E and I think we can have this on Fractal at the end
of the year, but this is not what I want to talk about today.&lt;/p&gt;
&lt;p&gt;In this meeting there was Fractal developers but we've also other people,
like the &lt;a href="https://github.com/mujx/nheko"&gt;nheko&lt;/a&gt; developer, mujx (nheko is a Qt matrix client). During the
meeting, mujx ask us about the cache storage that we're using, because for the
E2E is too important to don't lose any key, because that'll be catastrophic,
you won't be able to read room messages. So it's important to have a
transactional database storage for this information. They are using &lt;a href="http://www.lmdb.tech/doc/"&gt;LMDB&lt;/a&gt;,
I didn't know nothing about LMDB so after this meeting I start to about it.&lt;/p&gt;
&lt;h2&gt;LMDB&lt;/h2&gt;
&lt;p&gt;Lightning Memory-Mapped Database Manager (LMDB) is a key-value database, it's
memory mapped so it's fast, and also uses filesystem storage so we've
persistence. This database has transactions so it's safe to read/write from
different threads or process.&lt;/p&gt;
&lt;p&gt;In Fractal we're using a simple json file for cache, but this doesn't support
transactions and if the app crash or if something bad happens, we can lose data.
This method is simple, but is slow and insecure, so using LMDB will improve
Fractal in several ways.&lt;/p&gt;
&lt;p&gt;But LMDB is in memory and in Fractal we've a lot of interface code sharing the
app state, so we're passing the state between threads with copies and complex
data sharing. This can simplify the interface code because using LMDB for the
application global state will make this state accesible from different threads.&lt;/p&gt;
&lt;h2&gt;Testing LMDB&lt;/h2&gt;
&lt;p&gt;Fractal is written in Rust so I want to write some tests before start to use
this on Fractal. There's a simple &lt;a href="https://crates.io/crates/lmdb"&gt;LMDB rust crate&lt;/a&gt;, and I've been writting
an &lt;a href="https://gitlab.gnome.org/danigm/lmdb-rs-tests"&gt;example lib&lt;/a&gt; to test it.&lt;/p&gt;
&lt;p&gt;Basically what I've done is to write a simple trait that you can implement for
simple Rust structs so that struct can be stored and recover from the cache
with a simple method:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="nx"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Cacheable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TESTDB&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;b:{}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The struct should be serializable/deserializable with serde because in this
example I'm using &lt;a href="https://crates.io/crates/bincode"&gt;bincode&lt;/a&gt; to convert structs to [u8].&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Cacheable&lt;/code&gt; trait only have two required methods, the db and the key. The
db is the db name to use to store this struct instances and the key is the
key to use when storing a concrete instance.&lt;/p&gt;
&lt;p&gt;With this, we can store in the cache and query from the cache, using the key:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;format&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{}-basic&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;new&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;unwrap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;complex&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="cp"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;store&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;is_ok&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;b:1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;unwrap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;len&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;One string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;to_string&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Second string&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;to_string&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nt"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;store&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;B&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;b:1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;unwrap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="nt"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;len&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;assert_eq&lt;/span&gt;&lt;span class="o"&gt;!(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;complex&lt;/span&gt;&lt;span class="cp"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="cp"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;..&lt;/span&gt;&lt;span class="cp"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;One string&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is thread safe, so we can read from the cache from different threads and
we'll always get the last version in the database.&lt;/p&gt;
&lt;p&gt;LMDB is a key-value database, so we don't have relations. This is not a real
problem for us because we can model the relations in the keys, for example, we
can store Room messages with keys like this "message:ROOMID:messageID" and then
we can iterate over all objects with this prefix: "message:ROOMID" that will
give us all room messages. Something like this should work:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;format&lt;/span&gt;&lt;span class="o"&gt;!(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;message:{}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;iter&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;m&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;fetched&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;database,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;what&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;we&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;Continue(true)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;LMDB in Fractal data model&lt;/h2&gt;
&lt;p&gt;We're thinking about moving the Fractal data model from the AppOp struct to
a new crate, independent of the UI, to simplify the UI code and to be able
to use the same data model from different UIs (we wan't to split fractal in
two different apps), &lt;a href="https://blogs.gnome.org/jsparber/2018/06/17/refactor-backend-and-ui/"&gt;Julian wrote about this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think that we can use the LMDB cache to store this new app state that we
want to have and this will simplify a lot our code, because we will share the
same state and this state will be persisted in filesystem.&lt;/p&gt;
&lt;p&gt;I'll start to write a new crate for Fractal to start to move all the app state
to this new crate. I think I can write a generic tool to simplify the LMDB use,
maybe I'll publish another crate in crates.io and use that in Fractal, but I
need to think a little more about the pattern to follow.&lt;/p&gt;
&lt;p&gt;I write a lot of web code in my day to day work, and I've been working with
react+redux. This LMDB cache thing in Fractal reminds me a lot to the redux
store and I want to follow a similar pattern so we can have only an app state
and only a way to update this state.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 30 Jun 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-06-30:/lmdb.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>lmdb</category><category>cache</category><category>fractal</category></item><item><title>Stickers in Riot</title><link>https://danigm.net/stickers-in-riot.html</link><description>&lt;p&gt;Yesterday I read a &lt;a href="https://medium.com/@RiotChat/stickers-are-here-introducing-riot-im-0-15-for-web-desktop-284c32b93acc"&gt;blog post&lt;/a&gt; about the new Riot.im Stickers. This is
not a matrix.org feature, it's implemented as a widget, when you send an
sticker you're sending a new event with the type "m.sticker", which is
similar to the "m.image" event.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/stickers.gif" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;The matrix.org protocol is flexible so this is a good example of how to add
new features to the clients that uses matrix without the need to change the
protocol.&lt;/p&gt;
&lt;p&gt;This is not a core feature because you can send images, but I think this is
great and add a simple way to show reactions for the users, so as I was
reading I thought that we can add this to &lt;a href="https://wiki.gnome.org/Apps/Fractal"&gt;Fractal&lt;/a&gt;, so I started to
read how we can add support for this.&lt;/p&gt;
&lt;h2&gt;Reading the doc&lt;/h2&gt;
&lt;p&gt;The first thing to implement a feature is to read the specifications or the
technical documentation so we can know what is needed... But there's no
&lt;a href="https://github.com/matrix-org/matrix-doc/issues/1236"&gt;documentation yet about Stickers or widgets yet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is a problem because we can't implement a feature if we don't know
what we should do. But free software give us a great opportunity when this
happens, we've the &lt;a href="https://github.com/vector-im/riot-web"&gt;Riot&lt;/a&gt; source code so we can look at the code and
learn what they are doing.&lt;/p&gt;
&lt;h2&gt;Reading the code&lt;/h2&gt;
&lt;p&gt;Riot web is a javascript application that uses AJAX to communicate with
different server APIs so the first thing that I did to start to understand
the stickers thing was to open the firefox debugger and view how riot is
communicating with the server.&lt;/p&gt;
&lt;p&gt;From here I've learned that for stickers riot is asking to the
scalar.vector.im server. But I don't understand the whole thing with the
requests because riot does a lot of request to different APIs and I can't
isolate the stickers thing easily.&lt;/p&gt;
&lt;p&gt;To fill my understanding gap I go to the &lt;a href="https://github.com/matrix-org/matrix-js-sdk"&gt;matrix-js-sdk&lt;/a&gt; and
&lt;a href="https://github.com/matrix-org/matrix-react-sdk"&gt;matrix-react-sdk&lt;/a&gt; and I did a quick grep to the source code looking for
the API calls that I've view in firefox. With this I can understand the
full stickers process.&lt;/p&gt;
&lt;h2&gt;Writing an example&lt;/h2&gt;
&lt;p&gt;To say that I know how this is working, it's not enough the code reading.
To make sure that I've understood the whole process I need to write a
simple program that does all the process and then I can say that I
understand this.&lt;/p&gt;
&lt;p&gt;So I started to write a &lt;a href="https://github.com/danigm/matrix-stickers-example"&gt;simple python script&lt;/a&gt; using requests. This
simple script does the request to the server and list all the stickers json
so I can say that I'm able to communicate with the API.&lt;/p&gt;
&lt;h2&gt;Stickers in Fractal&lt;/h2&gt;
&lt;p&gt;After this small research I'm able to implement an initial sticker support
for Fractal. I'll try to add a simple way to show and use stickers and a
way to render stickers in the messages history.&lt;/p&gt;
&lt;p&gt;If there's no secret problems we'll have a basic stickers support in
Fractal soon.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 19 May 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-05-19:/stickers-in-riot.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>fractal</category></item><item><title>Fractal Hackfest, Strasbourg (day 2)</title><link>https://danigm.net/fractal-hackfest2.html</link><description>&lt;p&gt;The second day of Fractal hackfest was really productive, we talk about a
lot of topics and takes some important decisions.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-hackfest2.png" width="100%"/&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;h2&gt;E2E Encryption&lt;/h2&gt;
&lt;p&gt;The encryption is a needed feature but encryption is hard to do in rooms.
Matrix uses public-key cryptography, for rooms they are using &lt;a href="https://git.matrix.org/git/olm/about/docs/megolm.rst"&gt;Megolm&lt;/a&gt;,
that's a protocol to exchange encrypted messages with more than one and
share that message keys in a one-to-one secure communication.&lt;/p&gt;
&lt;p&gt;I don't know a lot about this E2E because for me it's more important to
have the client working with a basic functionality before the encryption.
So you should read the official doc because maybe this that I'm writing
here is completely wrong.&lt;/p&gt;
&lt;p&gt;To do all this E2E key sharing, client side encryption and communication,
Riot has three different implementations of the &lt;em&gt;same&lt;/em&gt; lib, so they have
this code in the JavaScript SDK, the same ported to iOS version in
ObjectiveC and the same ported to Android in Java. Below this lib there's
the libolm that does the real encryption.&lt;/p&gt;
&lt;p&gt;For the future it should be a good solution to have only one lib that does
the encryption thing, so we need to push in that direction and maybe
matrix.org and Purism can pay for that work to be done.&lt;/p&gt;
&lt;p&gt;From Fractal, we've two options or wait until we've the official E2E
C/C++-lib or something, or try to port the iOS lib to Rust or C to use in
our codebase. I think that the best solution is the first one, in any case,
we'll need to wait some time to view E2E in Fractal.&lt;/p&gt;
&lt;h2&gt;Calls&lt;/h2&gt;
&lt;p&gt;Calls are easy compared to the Encryption thing, the &lt;a href="https://matrix.org/docs/spec/client_server/r0.3.0.html#voice-over-ip"&gt;protocol is well documented&lt;/a&gt;
and it's really simple. The hard part here is that the call is implemented
using the webRTC and that is a web protocol... But it seems that there's
something done in this case with GStreamer working with webRTC.&lt;/p&gt;
&lt;h2&gt;Multiple accounts&lt;/h2&gt;
&lt;p&gt;We was talking yesterday about the multiple accounts problem, if we want to
support and in that case what solution we should take.&lt;/p&gt;
&lt;p&gt;We've two possible solutions, one is to support multiple accounts at the
same time and a way to change between accounts, and the other one is to
open a window for each account.&lt;/p&gt;
&lt;p&gt;At the end we think that the best solution could be to open a separated
window for each account so if you've multiple accounts, at first you should
choose what account you want to open and then from the menu you can open a
new &lt;em&gt;window&lt;/em&gt; and go to the account selection menu again.&lt;/p&gt;
&lt;p&gt;We need to work in the design of this feature and it's not confirmed but I
think that this was the best solution, because it's simple and powerful.&lt;/p&gt;
&lt;h2&gt;Communities&lt;/h2&gt;
&lt;p&gt;Communities are something really new in the Matrix.org protocol and it's
not well documented yet and there's some functionality that is not
implemented so we decide to wait until this functionality is more mature to
view how we can integrate this in Fractal.&lt;/p&gt;
&lt;p&gt;Communities today are a group of rooms and a group of people and a full
html description to show in the community &lt;em&gt;page&lt;/em&gt;. There's other features
like show a &lt;em&gt;label&lt;/em&gt; for each member of the community or group, so I think
these features are cool. We'll need to support some parts of the
communities protocol soon.&lt;/p&gt;
&lt;h2&gt;Google Summer of Code&lt;/h2&gt;
&lt;p&gt;&lt;center&gt;
    &lt;p class="img"&gt;
    &lt;img src="/pictures/fractal-hackfest1.png" width="100%" /&gt;
    &lt;/p&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;As I said this year we've two students working on Fractal thanks to Google,
as part of the &lt;a href="https://summerofcode.withgoogle.com/organizations/5900447454330880/"&gt;GNOME organization&lt;/a&gt;, and both students comes to the
Hackfest so thank you Julian and Eisha for coming and for the work done
before the GSoC and for sure for the work you'll be doing during the
summer.&lt;/p&gt;
&lt;p&gt;We're talking about how we'll be organizing, I'm the mentor of both but
we'll have the help from Tobias Bernard and from Alexandre Franke so we'll
have a summer hacking group to improve Fractal a lot.&lt;/p&gt;
&lt;p&gt;We'll have a weekly meeting to talk about what we're doing and what to do
in the next week.&lt;/p&gt;
&lt;p&gt;For this first week we'll start with two needed features. Julian will be
working in the user preference dialog and Eisha will start to work in the
internationalization (i18n) because it's not easy to use gettext in rust
and we want to translate the app to make it more accessible for everyone in
the world.&lt;/p&gt;
&lt;h2&gt;Hacking a bit&lt;/h2&gt;
&lt;p&gt;In the meantime, between functionality discussion I was working in a way to
use Fractal without SecretService, so if you're using Fractal outside GNOME
or KDE you can configure it to store password and token in a plain text.
We shouldn't store passwords in a clear text file, but I think that this
will simplify the day for many people that wants to use Fractal but don't
want to write the user and password every time.&lt;/p&gt;
&lt;p&gt;This is not finished yet, but we'll have this very soon, and with this it's
possible to implement other password &amp;amp; token storage services and make it
configurable, so maybe in the future someone implements the storage for
MacOS or something.&lt;/p&gt;
&lt;h2&gt;Holidays&lt;/h2&gt;
&lt;p&gt;This was my last day in the hackfest, I've the travel back to Spain the
Sunday but I'll do some tourism here so I need to leave the people, they
will continue working the Saturday and the Sunday.&lt;/p&gt;
&lt;p&gt;This have been the first Fractal hackfest and it was a great experience, to
meet other people working on it and to talk about the future of the
application. It's really cool that with less than a year project we've now
a big community, so thank you all form coming and see you on GUADEC.&lt;/p&gt;
&lt;p&gt;There's a lot of work to do, but we're lucky because we're two students
working in Fractal this summer and the people from &lt;a href="https://matrix.org/blog/2018/05/11/this-week-in-matrix-2018-05-11/"&gt;Matrix.org&lt;/a&gt; and
&lt;a href="https://puri.sm/posts/librem5-progress-report-11/"&gt;Purism&lt;/a&gt; are working with us, so thank you all.&lt;/p&gt;
&lt;p&gt;I want to thank again the GNOME foundation and GNOME people for make this
possible and to my company &lt;a href="https://wadobo.com"&gt;Wadobo&lt;/a&gt;, for let me spend some time working
on this great project.&lt;/p&gt;
&lt;p&gt;&lt;center&gt;
    &lt;a href="https://wadobo.com"&gt;
        &lt;img src="/pictures/wadobo-mini.png" /&gt;
    &lt;/a&gt;
&lt;/center&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 12 May 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-05-12:/fractal-hackfest2.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>fractal</category><category>wadobo</category></item><item><title>Fractal Hackfest, Strasbourg (day 1)</title><link>https://danigm.net/fractal-hackfest1.html</link><description>&lt;p&gt;Yesterday was the first day in the first &lt;a href="https://wiki.gnome.org/Hackfests/Fractal2018"&gt;Fractal Hackfest&lt;/a&gt;. I'll try to
write an small blog post every day to share the development with the world.&lt;/p&gt;
&lt;p&gt;My travel to Strasbourg was not an easy travel because I've to take two
flights to get here from Málaga so a long day travelling.&lt;/p&gt;
&lt;p&gt;I met with Mathew from Matrix.org at the London airport because we took the
same flight to here and it was really cool to meet him in person and we
talk a little about the current Matrix situation.&lt;/p&gt;
&lt;p&gt;I've met the other Fractal people and collaborators at the event, and it's
great that people from Purism, Matrix, Gnome and the two GSoC students come
here to work together in this great application.&lt;/p&gt;
&lt;h2&gt;Barbecue and Banquet&lt;/h2&gt;
&lt;p&gt;We've spend almost all the day talking about the different uses cases for
the messaging problem and Tobias has come with a proposal for split the
current application in two because the use case is totally different.&lt;/p&gt;
&lt;p&gt;We've the first case when you use the app to talk to family, friends and
small groups of people where all conversations are private and you want to
read almost every message there, we call that situation the "Barbecue".&lt;/p&gt;
&lt;p&gt;The second case is the one where you use the messaging app to talk in a
public or private room with a lot of people, where you are receiving a lot
of messages but you don't want to follow the full conversation you'll talk
with someone or read a topic conversation, we call this one the "Banquet".&lt;/p&gt;
&lt;p&gt;Currently Matrix clients are more oriented to the Banquet use case so
there's a lot of people and communities using it for this. The idea behind
this split is to provide two apps to the user to make both cases possible.&lt;/p&gt;
&lt;p&gt;We need to work on this idea but it seems that is the correct way to go.&lt;/p&gt;
&lt;h2&gt;Initial Syn speed up&lt;/h2&gt;
&lt;p&gt;At the afternoon. We've reviewing the current initial sync filter and
detect some points to speedup.&lt;/p&gt;
&lt;p&gt;I changed the filter and the initial sync goes a lot faster. I've removed
the member events from the initial sync so there's a lot of less
information to retrieve from the server.&lt;/p&gt;
&lt;p&gt;This little change breaks the one to one room name and default avatars
because that name is calculated using the members in the room so I need to
fix that making the name calculation a backend process. So now if a room
has no name, We'll ask for the room member list and when we've that we'll
calculate the room name with the same algorithm, if there're only two
people, we'll use the other person username, if there's three people we'll
use "one and another" and if there're more we'll use "one and others".&lt;/p&gt;
&lt;h2&gt;Gnome dinner&lt;/h2&gt;
&lt;p&gt;We've a good dinner sponsored by the gnome foundation. Thank you so much.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 11 May 2018 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2018-05-11:/fractal-hackfest1.html</guid><category>blog</category><category>gnome</category><category>rust</category><category>programming</category><category>fractal</category><category>wadobo</category></item></channel></rss>