In October 2021, I got my first pull request in a major open-source project merged. Here's what happened, and how I did it -
Meilisearch is a "powerful, fast, open-source, easy to use and deploy search engine. Both searching and indexing are highly customizable. Features such as typo-tolerance, filters, and synonyms are provided out-of-the-box".
To put it simply, Meilisearch allows you to implement fast search at scale, and does it in Rust. Take a look at this GIF and see it in action. It's safe to say it blew my mind the first time around.
What was the Issue?
Meilisearch has a browser-accessible dashboard that helps you test the implementation as and when you install it. This dashboard is usually how most people first interact with Meilisearch, and it's where the demo GIF comes from. Given the above, it's easy to understand why it's a critical user-experience component of the application.
I, on the other hand, was (and still am) a curious rustacean looking to better my knowledge, so I was naturally interested when I saw this issue open with the
good first issue label. I jumped at the chance to figure this out, and then I cloned the repository. It's the largest codebase I've interacted with, and I was naturally somewhat intimidated by what was going on.
This repository didn't deal with the core implementation of search - that's taken care of by milli - to quote, "The code in this repository is only concerned with exposing the HTTP API, managing multiple indexes, and handling the update store".
After I cloned it, I got started with trying to fix this error. There were no error messages while this project ran, which meant that this bug would be harder to sniff out.
The Debugging Process
All I had to start with were a bunch of errors in the browser console.
From my experience with frameworks such as Django and Flask, This usually meant some file wasn't being served properly. Here's what I did to dig a little deeper -
I Ran the test suite, and All the tests passed
This was simple enough - all I had to do was run
$ cargo test
Rust has some pretty neat features baked directly into the toolchain, which helps you write tests pretty easily. As you see above, it's also super easy to run a test suite.
Investigated the built output
I then proceeded to build the project, for which all I had to do was run
$ cargo build
I found the build output at
/meilisearch-http/target/rls/debug/build/meilisearch-http-a6e2793cb../out/mini-dashboard/. This consisted of a bunch of static files pulled from an external source, which were then served using the Actix Web Framework.
Since there were a bunch of static files wired up, I used the npm package
serveto run a temporary web server here, which served all files correctly.
# if you haven't got it installed already $ npm install -g serve # installs this globally $ serve
This confirmed my initial suspicion that it wasn't a problem with the dashboard itself, but rather with how it was being served.
I then Looked into how the server was setup.
At this point I used simple print debugging to ensure that the paths were generated correctly, along with their MIME types. This, too, is relatively simple - you can use the
println!()macro to easily print the resource in question.
All seemed to be in order. I also tried using the
format!macro to prepend a slash onto the file path for each file, but this didn't make a difference at all.
At this point I noticed that the
index.htmlfile was the only file being served correctly.
Taking a look into the route configuration, I noticed that the route for the index file was configured like this -
config.service(web::resource("/").route( web::get().to(move || HttpResponse::Ok().content_type(mime_type).body(data)), ));
as opposed to
scope = scope.service(web::resource(path).route( web::get().to(move || HttpResponse::Ok().content_type(mime_type).body(data)), ));
Given I'm not too familiar with Actix, I took the approach of registering the routes for the static files the same way as the
index.htmlfile, replacing the above block with the following to serve static files -
config.service(web::resource(path).route( web::get().to(move || HttpResponse::Ok().content_type(mime_type).body(data)), ));
localhost:7700 now produced the dashboard properly.
Why did this change work?
Given I haven't explored Actix in depth, I'm probably not the best person to answer this question, but if I had to take a guess, I'd say it has to do with how Actix internals decided route registration for static assets should take place. It also probably calls for special handling of the index page at
/, and this caused the issue.
I'll be back with more information on this when I've learnt more!
This was my first contribution of any sort to a major open-source project, and I'm glad it was a Rust project! It's a really cool language, and I'm glad I got to learn more about it. The maintainers were super nice, and this made me feel welcome! I was happy when this PR got merged, and have, since then, felt a little more confident about my ability to navigate large codebases which I have no familiarity with. This was fun!
I look forward to contributing to more, larger open-source projects!