4850: Use a fixed date format regardless of features r=irevoire a=dureuill

# Pull Request

## Related issue
Fixes #4844 

## What does this PR do?

Given the following script: 
```
cargo run -- --db-path meili.ms
sleep 3
curl -s -X POST http://127.0.0.1:7700/indexes -H 'Content-Type: application/json' --data-binary '{"uid": "movies", "primaryKey": "id"}'
sleep 3
cargo run  -p meilisearch --db-path meili.ms
sleep 3
curl -s -X POST http://127.0.0.1:7700/indexes/movies/search -H 'Content-Type: application/json' --data-binary '{}'
```

- Before this PR, the final search returns a decoding error.
- After this PR, the search completes successfully

### Technical standpoint

This PR fixes two locations where the formatting of dates were dependent on the feature set of the `time` crate.

1. The `IndexStats` had two fields without the serialization format specified
2. More subtly, the index dates (`createdAt,` `updatedAt`) were using value remapping in the main DB to `SerdeJson<OffsetDateTime>`, which was using whatever default format was available. This was fixed by creating a local `OffsetDateTime` wrapper that would specify the serialization format 

Co-authored-by: Louis Dureuil <louis@meilisearch.com>
This commit is contained in:
meili-bors[bot] 2024-07-31 15:32:26 +00:00 committed by GitHub
commit 420c33132c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 22 additions and 15 deletions

View File

@ -108,8 +108,10 @@ pub struct IndexStats {
/// Association of every field name with the number of times it occurs in the documents. /// Association of every field name with the number of times it occurs in the documents.
pub field_distribution: FieldDistribution, pub field_distribution: FieldDistribution,
/// Creation date of the index. /// Creation date of the index.
#[serde(with = "time::serde::rfc3339")]
pub created_at: OffsetDateTime, pub created_at: OffsetDateTime,
/// Date of the last update of the index. /// Date of the last update of the index.
#[serde(with = "time::serde::rfc3339")]
pub updated_at: OffsetDateTime, pub updated_at: OffsetDateTime,
} }

View File

@ -9,7 +9,6 @@ use heed::{CompactionOption, Database, RoTxn, RwTxn, Unspecified};
use roaring::RoaringBitmap; use roaring::RoaringBitmap;
use rstar::RTree; use rstar::RTree;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use crate::documents::PrimaryKey; use crate::documents::PrimaryKey;
use crate::error::{InternalError, UserError}; use crate::error::{InternalError, UserError};
@ -173,8 +172,8 @@ impl Index {
pub fn new_with_creation_dates<P: AsRef<Path>>( pub fn new_with_creation_dates<P: AsRef<Path>>(
mut options: heed::EnvOpenOptions, mut options: heed::EnvOpenOptions,
path: P, path: P,
created_at: OffsetDateTime, created_at: time::OffsetDateTime,
updated_at: OffsetDateTime, updated_at: time::OffsetDateTime,
) -> Result<Index> { ) -> Result<Index> {
use db_name::*; use db_name::*;
@ -256,22 +255,22 @@ impl Index {
} }
pub fn new<P: AsRef<Path>>(options: heed::EnvOpenOptions, path: P) -> Result<Index> { pub fn new<P: AsRef<Path>>(options: heed::EnvOpenOptions, path: P) -> Result<Index> {
let now = OffsetDateTime::now_utc(); let now = time::OffsetDateTime::now_utc();
Self::new_with_creation_dates(options, path, now, now) Self::new_with_creation_dates(options, path, now, now)
} }
fn set_creation_dates( fn set_creation_dates(
env: &heed::Env, env: &heed::Env,
main: Database<Unspecified, Unspecified>, main: Database<Unspecified, Unspecified>,
created_at: OffsetDateTime, created_at: time::OffsetDateTime,
updated_at: OffsetDateTime, updated_at: time::OffsetDateTime,
) -> heed::Result<()> { ) -> heed::Result<()> {
let mut txn = env.write_txn()?; let mut txn = env.write_txn()?;
// The db was just created, we update its metadata with the relevant information. // The db was just created, we update its metadata with the relevant information.
let main = main.remap_types::<Str, SerdeJson<OffsetDateTime>>(); let main = main.remap_types::<Str, SerdeJson<OffsetDateTime>>();
if main.get(&txn, main_key::CREATED_AT_KEY)?.is_none() { if main.get(&txn, main_key::CREATED_AT_KEY)?.is_none() {
main.put(&mut txn, main_key::UPDATED_AT_KEY, &updated_at)?; main.put(&mut txn, main_key::UPDATED_AT_KEY, &OffsetDateTime(updated_at))?;
main.put(&mut txn, main_key::CREATED_AT_KEY, &created_at)?; main.put(&mut txn, main_key::CREATED_AT_KEY, &OffsetDateTime(created_at))?;
txn.commit()?; txn.commit()?;
} }
Ok(()) Ok(())
@ -371,7 +370,7 @@ impl Index {
wtxn: &mut RwTxn<'_>, wtxn: &mut RwTxn<'_>,
primary_key: &str, primary_key: &str,
) -> heed::Result<()> { ) -> heed::Result<()> {
self.set_updated_at(wtxn, &OffsetDateTime::now_utc())?; self.set_updated_at(wtxn, &time::OffsetDateTime::now_utc())?;
self.main.remap_types::<Str, Str>().put(wtxn, main_key::PRIMARY_KEY_KEY, primary_key) self.main.remap_types::<Str, Str>().put(wtxn, main_key::PRIMARY_KEY_KEY, primary_key)
} }
@ -1323,7 +1322,7 @@ impl Index {
} }
/// Returns the index creation time. /// Returns the index creation time.
pub fn created_at(&self, rtxn: &RoTxn<'_>) -> Result<OffsetDateTime> { pub fn created_at(&self, rtxn: &RoTxn<'_>) -> Result<time::OffsetDateTime> {
Ok(self Ok(self
.main .main
.remap_types::<Str, SerdeJson<OffsetDateTime>>() .remap_types::<Str, SerdeJson<OffsetDateTime>>()
@ -1331,11 +1330,12 @@ impl Index {
.ok_or(InternalError::DatabaseMissingEntry { .ok_or(InternalError::DatabaseMissingEntry {
db_name: db_name::MAIN, db_name: db_name::MAIN,
key: Some(main_key::CREATED_AT_KEY), key: Some(main_key::CREATED_AT_KEY),
})?) })?
.0)
} }
/// Returns the index last updated time. /// Returns the index last updated time.
pub fn updated_at(&self, rtxn: &RoTxn<'_>) -> Result<OffsetDateTime> { pub fn updated_at(&self, rtxn: &RoTxn<'_>) -> Result<time::OffsetDateTime> {
Ok(self Ok(self
.main .main
.remap_types::<Str, SerdeJson<OffsetDateTime>>() .remap_types::<Str, SerdeJson<OffsetDateTime>>()
@ -1343,18 +1343,19 @@ impl Index {
.ok_or(InternalError::DatabaseMissingEntry { .ok_or(InternalError::DatabaseMissingEntry {
db_name: db_name::MAIN, db_name: db_name::MAIN,
key: Some(main_key::UPDATED_AT_KEY), key: Some(main_key::UPDATED_AT_KEY),
})?) })?
.0)
} }
pub(crate) fn set_updated_at( pub(crate) fn set_updated_at(
&self, &self,
wtxn: &mut RwTxn<'_>, wtxn: &mut RwTxn<'_>,
time: &OffsetDateTime, time: &time::OffsetDateTime,
) -> heed::Result<()> { ) -> heed::Result<()> {
self.main.remap_types::<Str, SerdeJson<OffsetDateTime>>().put( self.main.remap_types::<Str, SerdeJson<OffsetDateTime>>().put(
wtxn, wtxn,
main_key::UPDATED_AT_KEY, main_key::UPDATED_AT_KEY,
time, &OffsetDateTime(*time),
) )
} }
@ -1681,6 +1682,10 @@ pub struct IndexEmbeddingConfig {
pub user_provided: RoaringBitmap, pub user_provided: RoaringBitmap,
} }
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
struct OffsetDateTime(#[serde(with = "time::serde::rfc3339")] time::OffsetDateTime);
#[cfg(test)] #[cfg(test)]
pub(crate) mod tests { pub(crate) mod tests {
use std::collections::HashSet; use std::collections::HashSet;