Skip to content

Commit daf988c

Browse files
committed
do work on caching
1 parent 71c17dd commit daf988c

File tree

14 files changed

+243
-71
lines changed

14 files changed

+243
-71
lines changed

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@ sea-orm = { version = "^0.12.0", features = [
4242
tower-http = { version = "0.5.1", features = ["cors", "trace"] }
4343
tracing-tree = { version = "0.3.0", features = ["time"] }
4444
futures = "0.3.30"
45+
dashmap = "5.5.3"
46+
parking_lot = "0.12.1"

pwm-web/app/dash/layout.tsx

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,42 @@ import { SearchBar } from "./searchbar";
66
import { NewItem } from "./newitem";
77

88
export default function DashboardLayout({
9-
children,
9+
children,
1010
}: Readonly<{
11-
children: React.ReactNode;
11+
children: React.ReactNode;
1212
}>) {
13-
return (
14-
<div className="w-full h-screen ">
15-
<Image
16-
className="w-full h-full absolute inset-0 object-cover object-right "
17-
alt={"background image"}
18-
src={BackgroundImage}
19-
/>
20-
<div
21-
className="absolute inset-4 rounded-lg "
22-
style={{ boxShadow: "rgb(0 0 0 / 34%) 0px 8px 11px 3px" }}
23-
>
24-
<div className="flex h-full divide-white dark:divide-black divide-x-2 divide-y-0 border-pink-400/50 dark:border-neutral-500/50 border rounded-lg">
25-
<div className="h-full w-full max-w-32 md:max-w-screen-sm md:flex-[.20] rounded-l-lg backdrop-blur-lg bg-pink-300/30 dark:bg-neutral-800/70">
26-
<Suspense fallback={<p>load...</p>}>
27-
<SideBar />
28-
</Suspense>
29-
</div>
30-
<div className="flex-1 h-full">
31-
<div className="h-full flex flex-col bg-neutral-200/90 backdrop-blur-xl dark:bg-neutral-800/90 rounded-r-lg ">
32-
<div className="gap-20 border-b border-white dark:border-black py-4 px-10 flex items-center justify-around w-full">
33-
<SearchBar />
34-
<NewItem />
35-
</div>
36-
<div className="h-full">
37-
{/* <Suspense> */}
38-
{children}
39-
{/* </Suspense> */}
40-
</div>
41-
</div>
42-
</div>
43-
</div>
44-
</div>
45-
</div>
46-
);
13+
return (
14+
<div className="w-full h-screen ">
15+
{/* <Image */}
16+
{/* className="w-full h-full absolute inset-0 object-cover object-right " */}
17+
{/* alt={"background image"} */}
18+
{/* src={BackgroundImage} */}
19+
{/* /> */}
20+
<div
21+
className="absolute inset-4 rounded-lg "
22+
style={{ boxShadow: "rgb(0 0 0 / 34%) 0px 8px 11px 3px" }}
23+
>
24+
<div className="flex h-full divide-white dark:divide-black divide-x-2 divide-y-0 border-pink-400/50 dark:border-neutral-500/50 border rounded-lg">
25+
<div className="h-full w-full max-w-32 md:max-w-screen-sm md:flex-[.20] rounded-l-lg backdrop-blur-lg bg-pink-300/30 dark:bg-neutral-800/70">
26+
<Suspense fallback={<p>load...</p>}>
27+
<SideBar />
28+
</Suspense>
29+
</div>
30+
<div className="flex-1 h-full">
31+
<div className="h-full flex flex-col bg-neutral-200/90 backdrop-blur-xl dark:bg-neutral-800/90 rounded-r-lg ">
32+
<div className="gap-20 border-b border-white dark:border-black py-4 px-10 flex items-center justify-around w-full">
33+
<SearchBar />
34+
<NewItem />
35+
</div>
36+
<div className="h-full">
37+
{/* <Suspense> */}
38+
{children}
39+
{/* </Suspense> */}
40+
</div>
41+
</div>
42+
</div>
43+
</div>
44+
</div>
45+
</div>
46+
);
4747
}

pwm-web/app/dash/sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export async function SideBar() {
2828
<div className="flex flex-col">
2929
<Button
3030
variant={"ghost"}
31-
className="gap-5 h-12 text-xl text-left hover:bg-neutral-100/10 items-center justify-start hover:text-white"
31+
className="!bg-neutral-400/10 gap-5 h-12 text-xl text-left hover:bg-neutral-100/10 items-center justify-start hover:text-white"
3232
>
3333
<Image
3434
className="w-auto h-full object-contain shadow-md select-none aspect-square rounded-full"

pwm-web/components/app/clientSidebarFolders.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"use client";
2-
import { useQuery, useQueryClient } from "@tanstack/react-query";
2+
import { QueryClient, useQuery, useQueryClient } from "@tanstack/react-query";
33
import { Button } from "../ui/button";
44
import { ServerGetFolders } from "@/lib/serverWrapper";
55
import { decryptWithConKey } from "@/lib/crypto";
@@ -20,12 +20,7 @@ export default function ClientSidebarFolders({
2020
queryKey: ["folders", access_token],
2121
});
2222
const queryClient = useQueryClient();
23-
const [selected_folder_id, set_folder_id] = useAtom(selectedFolderId);
2423
const [conkey] = useAtom(ContentKey);
25-
const setSelectedAsRoot = () => {
26-
queryClient.invalidateQueries({ queryKey: ["vault_items"] });
27-
set_folder_id("");
28-
};
2924
const joinedFolders = useMemo(() => {
3025
const levels: FolderTree = [];
3126
if (!data) return levels;
@@ -62,6 +57,7 @@ export default function ClientSidebarFolders({
6257
conkey={conkey}
6358
ignore={true}
6459
folder={{ name: "Root", folder_id: "", children: joinedFolders }}
60+
queryClient={queryClient}
6561
/>
6662
{/* {joinedFolders?.map((folder) => ( */}
6763
{/* <ClientSidebarFolder conkey={conkey} folder={folder} /> */}
@@ -70,12 +66,17 @@ export default function ClientSidebarFolders({
7066
</div>
7167
);
7268
}
73-
export function ClientSidebarFolder({
69+
function ClientSidebarFolder({
7470
folder,
7571
conkey,
7672
ignore = false,
77-
}: { ignore?: boolean; conkey: Uint8Array; folder: FolderWithChildren }) {
78-
const queryClient = useQueryClient();
73+
queryClient,
74+
}: {
75+
ignore?: boolean;
76+
conkey: Uint8Array;
77+
folder: FolderWithChildren;
78+
queryClient: QueryClient;
79+
}) {
7980
const [selected_folder_id, set_folder_id] = useAtom(selectedFolderId);
8081
const [open, setOpen] = useState(false);
8182
const decrypted_name = useMemo(() => {
@@ -92,7 +93,7 @@ export function ClientSidebarFolder({
9293
onClick={() => {
9394
setOpen(!open);
9495
}}
95-
variant={"ghost"}
96+
variant={"link"}
9697
>
9798
{open ? "▼" : "▶"}
9899
</Button>
@@ -103,7 +104,7 @@ export function ClientSidebarFolder({
103104
set_folder_id(folder.folder_id);
104105
}}
105106
variant={"ghost"}
106-
className={`gap-5 h-12 text-xl text-left hover:!bg-neutral-100/10 items-center justify-start hover:text-white ${
107+
className={`gap-5 flex-auto h-12 text-xl text-left hover:!bg-neutral-100/10 items-center justify-start hover:text-white ${
107108
selected_folder_id === folder.folder_id ? "!bg-neutral-400/10" : ""
108109
}`}
109110
>
@@ -114,6 +115,7 @@ export function ClientSidebarFolder({
114115
{open &&
115116
folder.children.map((child) => (
116117
<ClientSidebarFolder
118+
queryClient={queryClient}
117119
key={child.folder_id}
118120
conkey={conkey}
119121
folder={child}

runlocal.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#! /usr/bin/sh
22
docker compose up db -d
33
sea migrate
4-
RUST_LOG=none,pwm=debug cargo watch -x "run --release"
4+
RUST_LOG=none,pwm=debug,tower_http=debug cargo watch -x "run --release"

src/db/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use tracing::log::LevelFilter;
88

99
pub async fn init_db(mut database_opts: ConnectOptions) -> Result<DatabaseConnection, DbErr> {
1010
let new_db_opts = database_opts
11-
.sqlx_logging_level(LevelFilter::Info) // Or set SQLx log level
12-
.sqlx_logging(false);
11+
.sqlx_logging_level(LevelFilter::Debug) // Or set SQLx log level
12+
.sqlx_logging(true);
1313

1414
let pool = Database::connect(new_db_opts.clone()).await?;
1515

src/extractors/cacher.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use std::fmt::Debug;
2+
use std::ops::Deref;
3+
use std::sync::Arc;
4+
use std::{collections::HashMap, time::Duration};
5+
6+
use axum::extract::FromRef;
7+
use axum::http::StatusCode;
8+
use axum::Json;
9+
use chrono::Utc;
10+
use dashmap::mapref::one::{MappedRef, RefMut};
11+
use dashmap::{mapref::one::Ref, DashMap};
12+
use parking_lot::RwLock;
13+
use sea_orm::prelude::DateTimeUtc;
14+
use tokio::{sync::broadcast, time::Instant};
15+
use tracing::instrument;
16+
17+
use crate::PwmResponse;
18+
19+
#[derive(Debug)]
20+
pub struct Cacher<T: Send + Sync> {
21+
cached_values: DashMap<String, (T, DateTimeUtc)>,
22+
cache_expiry: Duration,
23+
last_cleaned: RwLock<Instant>,
24+
}
25+
impl<T: Send + Sync> Cacher<T> {
26+
pub fn new(cache_expiry: Duration) -> Self {
27+
Self {
28+
cached_values: DashMap::new(),
29+
cache_expiry,
30+
last_cleaned: RwLock::new(Instant::now()),
31+
}
32+
}
33+
pub fn invalidate(&self) {
34+
self.cached_values.clear();
35+
}
36+
pub fn invalidate_key(&self, key: &str) {
37+
self.cached_values.remove(key);
38+
}
39+
pub fn get(&self, key: &str) -> Option<MappedRef<'_, String, (T, DateTimeUtc), T>> {
40+
self.clean_cache();
41+
let x = self.cached_values.get(key).map(|x| x.map(|x| &x.0));
42+
x
43+
}
44+
pub fn insert(&self, key: String, value: T) {
45+
self.clean_cache();
46+
self.cached_values.insert(key, (value, Utc::now()));
47+
}
48+
fn get_no_check(&self, key: &str) -> Option<Ref<String, (T, DateTimeUtc)>> {
49+
self.cached_values.get(key)
50+
}
51+
fn clean_cache(&self) {
52+
let cleaning_due = {
53+
let last_cleaned = self.last_cleaned.read();
54+
let elapsed = last_cleaned.elapsed();
55+
(elapsed > self.cache_expiry, elapsed)
56+
};
57+
if cleaning_due.0 {
58+
self.cached_values
59+
.retain(|_k, _| cleaning_due.1 < self.cache_expiry);
60+
*self.last_cleaned.write() = Instant::now();
61+
}
62+
}
63+
}
64+
pub type ReadyCacheStore<T> = Result<T, (StatusCode, Json<PwmResponse>)>;
65+
#[derive(Debug, Clone)]
66+
pub struct ReadyCache<T: Send + Sync> {
67+
pub cache: Arc<Cacher<ReadyCacheStore<T>>>,
68+
pub in_progress: Arc<DashMap<String, broadcast::Sender<ReadyCacheStore<T>>>>,
69+
}
70+
impl<T: Send + Sync + Debug + Clone> ReadyCache<T> {
71+
pub fn new(cache_expiry: Duration) -> Self {
72+
Self {
73+
cache: Arc::new(Cacher::new(cache_expiry)),
74+
in_progress: Arc::new(DashMap::new()),
75+
}
76+
}
77+
#[instrument]
78+
pub fn start_processing(&self, key: String) {
79+
let (tx, rx) = broadcast::channel(1);
80+
self.in_progress.insert(key, tx.clone());
81+
}
82+
#[instrument]
83+
pub fn finish_processing(&self, key: &str, value: ReadyCacheStore<T>) {
84+
self.cache.insert(key.to_string(), value);
85+
let Some(ref_to_new) = self.cache.get_no_check(key) else {
86+
return;
87+
};
88+
let in_progress = self.in_progress.get(key).map(|x| x.clone());
89+
if let Some(in_progress) = in_progress {
90+
in_progress.send(ref_to_new.value().clone().0).ok();
91+
// self.in_progress.remove(key);
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)