Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: DashMap based catalog #1585

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ cargo-husky = { version = "1", features = ["user-hooks"], default-features = fal
clap = { version = "4", features = ["derive"] }
criterion = { version = "0.5", features = ["async_futures", "async_tokio", "html_reports"] }
ctor = "0.2"
dashmap = { version = "6.1.0", features = ["serde", "inline", "rayon"] }
deadpool-postgres = "0.14"
enum-display = "0.1"
env_logger = "0.11"
Expand Down
1 change: 1 addition & 0 deletions martin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ async-trait.workspace = true
bit-set = { workspace = true, optional = true }
brotli.workspace = true
clap.workspace = true
dashmap.workspace = true
deadpool-postgres = { workspace = true, optional = true }
enum-display.workspace = true
env_logger.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions martin/src/bin/martin-cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use log::{debug, error, info, log_enabled};
use martin::args::{Args, ExtraArgs, MetaArgs, OsEnv, SrvArgs};
use martin::srv::{merge_tilejson, DynTileSource};
use martin::{
append_rect, read_config, Config, MartinError, MartinResult, ServerState, Source, TileData,
append_rect, read_config, Config, MartinError, MartinResult, ServerState, TileInfoSource, TileData,
TileRect,
};
use martin_tile_utils::{bbox_to_xyz, TileCoord, TileInfo};
Expand Down Expand Up @@ -392,7 +392,7 @@ fn parse_encoding(encoding: &str) -> MartinCpResult<AcceptEncoding> {
async fn init_schema(
mbt: &Mbtiles,
conn: &mut SqliteConnection,
sources: &[&dyn Source],
sources: &[TileInfoSource],
tile_info: TileInfo,
args: &CopyArgs,
) -> Result<MbtType, MartinError> {
Expand Down
15 changes: 7 additions & 8 deletions martin/src/fonts/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::collections::hash_map::Entry;
use std::collections::{BTreeMap, HashMap};
use std::ffi::OsStr;
use std::fmt::Debug;
use std::path::PathBuf;
use std::sync::OnceLock;

use bit_set::BitSet;
use dashmap::{DashMap, Entry};
use itertools::Itertools as _;
use log::{debug, info, warn};
use pbf_font_tools::freetype::{Face, Library};
Expand Down Expand Up @@ -103,11 +102,11 @@ fn get_available_codepoints(face: &mut Face) -> Option<GetGlyphInfo> {

#[derive(Debug, Clone, Default)]
pub struct FontSources {
fonts: HashMap<String, FontSource>,
fonts: DashMap<String, FontSource>,
masks: Vec<BitSet>,
}

pub type FontCatalog = BTreeMap<String, CatalogFontEntry>;
pub type FontCatalog = DashMap<String, CatalogFontEntry>;

#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
Expand All @@ -125,7 +124,7 @@ impl FontSources {
return Ok(Self::default());
}

let mut fonts = HashMap::new();
let mut fonts = DashMap::new();
let lib = Library::init()?;

for path in config.iter() {
Expand All @@ -150,7 +149,7 @@ impl FontSources {
pub fn get_catalog(&self) -> FontCatalog {
self.fonts
.iter()
.map(|(k, v)| (k.clone(), v.catalog_entry.clone()))
.map(|v| (v.key().clone(), v.catalog_entry.clone()))
.sorted_by(|(a, _), (b, _)| a.cmp(b))
.collect()
}
Expand Down Expand Up @@ -242,7 +241,7 @@ pub struct FontSource {
fn recurse_dirs(
lib: &Library,
path: PathBuf,
fonts: &mut HashMap<String, FontSource>,
fonts: &mut DashMap<String, FontSource>,
is_top_level: bool,
) -> FontResult<()> {
let start_count = fonts.len();
Expand Down Expand Up @@ -275,7 +274,7 @@ fn recurse_dirs(

fn parse_font(
lib: &Library,
fonts: &mut HashMap<String, FontSource>,
fonts: &mut DashMap<String, FontSource>,
path: PathBuf,
) -> FontResult<()> {
static RE_SPACES: OnceLock<Regex> = OnceLock::new();
Expand Down
2 changes: 1 addition & 1 deletion martin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod config;
pub use config::{read_config, Config, ServerState};

mod source;
pub use source::{CatalogSourceEntry, Source, Tile, TileData, TileSources, UrlQuery};
pub use source::{CatalogSourceEntry, TileInfoSource, Source, Tile, TileData, TileSources, UrlQuery};

mod utils;
pub use utils::{
Expand Down
18 changes: 10 additions & 8 deletions martin/src/source.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::collections::{BTreeMap, HashMap};
use std::collections::HashMap;
use std::fmt::Debug;

use actix_web::error::ErrorNotFound;
use async_trait::async_trait;
use dashmap::DashMap;
use log::debug;
use martin_tile_utils::{TileCoord, TileInfo};
use serde::{Deserialize, Serialize};
Expand All @@ -18,8 +19,8 @@ pub type TileInfoSource = Box<dyn Source>;
pub type TileInfoSources = Vec<TileInfoSource>;

#[derive(Default, Clone)]
pub struct TileSources(HashMap<String, TileInfoSource>);
pub type TileCatalog = BTreeMap<String, CatalogSourceEntry>;
pub struct TileSources(DashMap<String, TileInfoSource>);
pub type TileCatalog = DashMap<String, CatalogSourceEntry>;

impl TileSources {
#[must_use]
Expand All @@ -37,16 +38,17 @@ impl TileSources {
pub fn get_catalog(&self) -> TileCatalog {
self.0
.iter()
.map(|(id, src)| (id.to_string(), src.get_catalog_entry()))
.map(|v| (v.key().to_string(), v.get_catalog_entry()))
.collect()
}

pub fn get_source(&self, id: &str) -> actix_web::Result<&dyn Source> {
pub fn get_source(&self, id: &str) -> actix_web::Result<TileInfoSource> {
Ok(self
.0
.get(id)
.ok_or_else(|| ErrorNotFound(format!("Source {id} does not exist")))?
.as_ref())
.value()
.clone())
}

Comment on lines +45 to 52
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets provide the rationale why the Box change was done:

The change to the boxed variant comes from here and cascades pretty far sadly.
Because accessing a value returns Ref<..> => a temporary variable

image

/// Get a list of sources, and the tile info for the merged sources.
Expand All @@ -56,7 +58,7 @@ impl TileSources {
&self,
source_ids: &str,
zoom: Option<u8>,
) -> actix_web::Result<(Vec<&dyn Source>, bool, TileInfo)> {
) -> actix_web::Result<(Vec<TileInfoSource>, bool, TileInfo)> {
let mut sources = Vec::new();
let mut info: Option<TileInfo> = None;
let mut use_url_query = false;
Expand All @@ -78,7 +80,7 @@ impl TileSources {

// TODO: Use chained-if-let once available
if match zoom {
Some(zoom) if Self::check_zoom(src, id, zoom) => true,
Some(zoom) if Self::check_zoom(&*src, id, zoom) => true,
None => true,
_ => false,
} {
Expand Down
65 changes: 39 additions & 26 deletions martin/src/sprites/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use std::collections::hash_map::Entry;
use std::collections::{BTreeMap, HashMap};
use std::fmt::Debug;
use std::path::PathBuf;

use dashmap::{DashMap, Entry};
use futures::future::try_join_all;
use log::{info, warn};
use serde::{Deserialize, Serialize};
use spreet::resvg::usvg::{Error as ResvgError, Options, Tree, TreeParsing};
use spreet::{
get_svg_input_paths, sprite_name, SpreetError, Sprite, Spritesheet, SpritesheetBuilder,
};
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::path::PathBuf;
use tokio::io::AsyncReadExt;

use self::SpriteError::{SpriteInstError, SpriteParsingError, SpriteProcessingError};
Expand Down Expand Up @@ -56,7 +55,7 @@ pub struct CatalogSpriteEntry {
pub images: Vec<String>,
}

pub type SpriteCatalog = BTreeMap<String, CatalogSpriteEntry>;
pub type SpriteCatalog = DashMap<String, CatalogSpriteEntry>;

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct SpriteConfig {
Expand All @@ -71,7 +70,7 @@ impl ConfigExtras for SpriteConfig {
}

#[derive(Debug, Clone, Default)]
pub struct SpriteSources(HashMap<String, SpriteSource>);
pub struct SpriteSources(DashMap<String, SpriteSource>);

impl SpriteSources {
pub fn resolve(config: &mut FileConfigEnum<SpriteConfig>) -> FileResult<Self> {
Expand Down Expand Up @@ -109,8 +108,8 @@ impl SpriteSources {

pub fn get_catalog(&self) -> SpriteResult<SpriteCatalog> {
// TODO: all sprite generation should be pre-cached
let mut entries = SpriteCatalog::new();
for (id, source) in &self.0 {
let entries = SpriteCatalog::new();
for source in &self.0 {
let paths = get_svg_input_paths(&source.path, true)
.map_err(|e| SpriteProcessingError(e, source.path.clone()))?;
let mut images = Vec::with_capacity(paths.len());
Expand All @@ -121,7 +120,7 @@ impl SpriteSources {
);
}
images.sort();
entries.insert(id.clone(), CatalogSpriteEntry { images });
entries.insert(source.key().clone(), CatalogSpriteEntry { images });
}
Ok(entries)
}
Expand All @@ -141,7 +140,7 @@ impl SpriteSources {
v.insert(SpriteSource { path });
}
}
};
}
}

/// Given a list of IDs in a format "id1,id2,id3", return a spritesheet with them all.
Expand All @@ -155,14 +154,17 @@ impl SpriteSources {

let sprite_ids = ids
.split(',')
.map(|id| {
self.0
.get(id)
.ok_or_else(|| SpriteError::SpriteNotFound(id.to_string()))
})
.map(|id| self.get(id))
.collect::<SpriteResult<Vec<_>>>()?;

get_spritesheet(sprite_ids.into_iter(), dpi, as_sdf).await
get_spritesheet(sprite_ids.iter(), dpi, as_sdf).await
}

fn get(&self, id: &str) -> SpriteResult<SpriteSource> {
match self.0.get(id) {
Some(v) => Ok(v.clone()),
None => Err(SpriteError::SpriteNotFound(id.to_string())),
}
}
}

Expand Down Expand Up @@ -247,19 +249,30 @@ mod tests {
//.sdf => generate sdf from png, add sdf == true
//- => does not generate sdf, omits sdf == true
for extension in ["_sdf", ""] {
test_src(sprites.values(), 1, "all_1", extension).await;
test_src(sprites.values(), 2, "all_2", extension).await;

test_src(sprites.get("src1").into_iter(), 1, "src1_1", extension).await;
test_src(sprites.get("src1").into_iter(), 2, "src1_2", extension).await;

test_src(sprites.get("src2").into_iter(), 1, "src2_1", extension).await;
test_src(sprites.get("src2").into_iter(), 2, "src2_2", extension).await;
let paths = sprites.iter().map(|v| v.path.clone()).collect::<Vec<_>>();
test_src(paths.iter(), 1, "all_1", extension).await;
test_src(paths.iter(), 2, "all_2", extension).await;

let src1_path = sprites
.get("src1")
.into_iter()
.map(|v| v.path.clone())
.collect::<Vec<_>>();
test_src(src1_path.iter(), 1, "src1_1", extension).await;
test_src(src1_path.iter(), 2, "src1_2", extension).await;

let src2_path = sprites
.get("src2")
.into_iter()
.map(|v| v.path.clone())
.collect::<Vec<_>>();
test_src(src2_path.iter(), 1, "src2_1", extension).await;
test_src(src2_path.iter(), 2, "src2_2", extension).await;
}
}

async fn test_src(
sources: impl Iterator<Item = &SpriteSource>,
sources: impl Iterator<Item = &PathBuf>,
pixel_ratio: u8,
filename: &str,
extension: &str,
Expand Down
4 changes: 2 additions & 2 deletions martin/src/srv/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub const RESERVED_KEYWORDS: &[&str] = &[
"reload", "sprite", "status",
];

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Catalog {
pub tiles: TileCatalog,
#[cfg(feature = "sprites")]
Expand Down Expand Up @@ -226,7 +226,7 @@ pub mod tests {
}

fn clone_source(&self) -> TileInfoSource {
unimplemented!()
Box::new(self.clone())
}

async fn get_tile(
Expand Down
4 changes: 2 additions & 2 deletions martin/src/srv/tiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use martin_tile_utils::{
use serde::Deserialize;

use crate::args::PreferredEncoding;
use crate::source::{Source, TileSources, UrlQuery};
use crate::source::{TileInfoSources, TileSources, UrlQuery};
use crate::srv::server::map_internal_error;
use crate::srv::SrvConfig;
use crate::utils::cache::get_or_insert_cached_value;
Expand Down Expand Up @@ -62,7 +62,7 @@ async fn get_tile(
}

pub struct DynTileSource<'a> {
pub sources: Vec<&'a dyn Source>,
pub sources: TileInfoSources,
pub info: TileInfo,
pub query_str: Option<&'a str>,
pub query_obj: Option<UrlQuery>,
Expand Down
Loading
Loading