mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-04 14:52:19 +00:00
Improved Qdrant server startup & client initialization (#770)
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
This commit is contained in:
1 parent
97e6003686
commit
cef1c99765
17 files changed
+443
-79
No files matched your search
@@ -25,7 +25,7 @@ use crate::dotnet::{cleanup_dotnet_server, start_dotnet_server, stop_dotnet_serv
|
||||
use crate::environment::{is_prod, is_dev, CONFIG_DIRECTORY, DATA_DIRECTORY};
|
||||
use crate::log::switch_to_file_logging;
|
||||
use crate::pdfium::PDFIUM_LIB_PATH;
|
||||
use crate::qdrant::{cleanup_qdrant, start_qdrant_server, stop_qdrant_server};
|
||||
use crate::qdrant::{start_qdrant_server, stop_qdrant_server};
|
||||
#[cfg(debug_assertions)]
|
||||
use crate::dotnet::create_startup_env_file;
|
||||
|
||||
@@ -148,7 +148,6 @@ pub fn start_tauri() {
|
||||
start_dotnet_server(app.handle().clone());
|
||||
}
|
||||
|
||||
cleanup_qdrant();
|
||||
start_qdrant_server(app.handle().clone());
|
||||
|
||||
info!(Source = "Bootloader Tauri"; "Reconfigure the file logger to use the app data directory {data_path:?}");
|
||||
|
||||
+73
-10
@@ -5,6 +5,7 @@ use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
use std::time::Duration;
|
||||
use log::{debug, error, info, warn};
|
||||
use once_cell::sync::Lazy;
|
||||
use axum::Json;
|
||||
@@ -18,6 +19,7 @@ use tauri::path::BaseDirectory;
|
||||
use tempfile::{TempDir, Builder};
|
||||
use crate::stale_process_cleanup::{kill_stale_process, log_potential_stale_process};
|
||||
use crate::sidecar_types::SidecarType;
|
||||
use tokio::time;
|
||||
use tauri_plugin_shell::process::{CommandChild, CommandEvent};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
|
||||
@@ -40,14 +42,24 @@ static API_TOKEN: Lazy<APIToken> = Lazy::new(|| {
|
||||
});
|
||||
|
||||
static TMPDIR: Lazy<Mutex<Option<TempDir>>> = Lazy::new(|| Mutex::new(None));
|
||||
static QDRANT_STATUS: Lazy<Mutex<QdrantStatus>> = Lazy::new(|| Mutex::new(QdrantStatus::default()));
|
||||
static QDRANT_STATUS: Lazy<Mutex<QdrantStatusInfo>> = Lazy::new(|| Mutex::new(QdrantStatusInfo::default()));
|
||||
|
||||
const PID_FILE_NAME: &str = "qdrant.pid";
|
||||
const SIDECAR_TYPE:SidecarType = SidecarType::Qdrant;
|
||||
const STARTUP_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
const STARTUP_CHECK_INTERVAL: Duration = Duration::from_millis(250);
|
||||
|
||||
#[derive(Clone, Copy, Default, Serialize, PartialEq, Eq)]
|
||||
enum QdrantStatus {
|
||||
#[default]
|
||||
Starting,
|
||||
Available,
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct QdrantStatus {
|
||||
is_available: bool,
|
||||
struct QdrantStatusInfo {
|
||||
status: QdrantStatus,
|
||||
unavailable_reason: Option<String>,
|
||||
}
|
||||
|
||||
@@ -60,6 +72,7 @@ fn qdrant_base_path() -> PathBuf {
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ProvideQdrantInfo {
|
||||
status: QdrantStatus,
|
||||
path: String,
|
||||
port_http: u16,
|
||||
port_grpc: u16,
|
||||
@@ -71,10 +84,12 @@ pub struct ProvideQdrantInfo {
|
||||
|
||||
pub async fn qdrant_port(_token: APIToken) -> Json<ProvideQdrantInfo> {
|
||||
let status = QDRANT_STATUS.lock().unwrap();
|
||||
let is_available = status.is_available;
|
||||
let current_status = status.status;
|
||||
let is_available = current_status == QdrantStatus::Available;
|
||||
let unavailable_reason = status.unavailable_reason.clone();
|
||||
|
||||
Json(ProvideQdrantInfo {
|
||||
status: current_status,
|
||||
path: if is_available {
|
||||
qdrant_base_path().to_string_lossy().to_string()
|
||||
} else {
|
||||
@@ -99,6 +114,14 @@ pub async fn qdrant_port(_token: APIToken) -> Json<ProvideQdrantInfo> {
|
||||
|
||||
/// Starts the Qdrant server in a separate process.
|
||||
pub fn start_qdrant_server<R: tauri::Runtime>(app_handle: tauri::AppHandle<R>){
|
||||
set_qdrant_starting();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
cleanup_qdrant();
|
||||
start_qdrant_server_internal(app_handle);
|
||||
});
|
||||
}
|
||||
|
||||
fn start_qdrant_server_internal<R: tauri::Runtime>(app_handle: tauri::AppHandle<R>){
|
||||
let path = qdrant_base_path();
|
||||
if !path.exists() && let Err(e) = fs::create_dir_all(&path){
|
||||
error!(Source="Qdrant"; "The required directory to host the Qdrant database could not be created: {}", e);
|
||||
@@ -117,12 +140,13 @@ pub fn start_qdrant_server<R: tauri::Runtime>(app_handle: tauri::AppHandle<R>){
|
||||
|
||||
let storage_path = path.join("storage").to_string_lossy().to_string();
|
||||
let snapshot_path = path.join("snapshots").to_string_lossy().to_string();
|
||||
let init_path = path.join(".qdrant-initialized").to_string_lossy().to_string();
|
||||
let init_path = path.join(".qdrant-initialized");
|
||||
let init_path_environment = init_path.to_string_lossy().to_string();
|
||||
|
||||
let qdrant_server_environment: HashMap<String, String> = HashMap::from_iter([
|
||||
(String::from("QDRANT__SERVICE__HTTP_PORT"), QDRANT_SERVER_PORT_HTTP.to_string()),
|
||||
(String::from("QDRANT__SERVICE__GRPC_PORT"), QDRANT_SERVER_PORT_GRPC.to_string()),
|
||||
(String::from("QDRANT_INIT_FILE_PATH"), init_path),
|
||||
(String::from("QDRANT_INIT_FILE_PATH"), init_path_environment),
|
||||
(String::from("QDRANT__STORAGE__STORAGE_PATH"), storage_path),
|
||||
(String::from("QDRANT__STORAGE__SNAPSHOTS_PATH"), snapshot_path),
|
||||
(String::from("QDRANT__TLS__CERT"), cert_path.to_string_lossy().to_string()),
|
||||
@@ -172,13 +196,24 @@ pub fn start_qdrant_server<R: tauri::Runtime>(app_handle: tauri::AppHandle<R>){
|
||||
};
|
||||
|
||||
let server_pid = child.pid();
|
||||
set_qdrant_available();
|
||||
info!(Source = "Bootloader Qdrant"; "Qdrant server process started with PID={server_pid}.");
|
||||
log_potential_stale_process(path.join(PID_FILE_NAME), server_pid, SIDECAR_TYPE);
|
||||
|
||||
// Save the server process to stop it later:
|
||||
*server_spawn_clone.lock().unwrap() = Some(child);
|
||||
|
||||
let init_path_clone = init_path.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if wait_for_qdrant_startup(init_path_clone).await {
|
||||
set_qdrant_available();
|
||||
info!(Source = "Qdrant"; "Qdrant is available.");
|
||||
} else {
|
||||
let reason = "Qdrant did not become available within the startup timeout.".to_string();
|
||||
error!(Source = "Qdrant"; "{reason}");
|
||||
set_qdrant_unavailable(reason);
|
||||
}
|
||||
});
|
||||
|
||||
// Log the output of the Qdrant server:
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
@@ -200,10 +235,18 @@ pub fn start_qdrant_server<R: tauri::Runtime>(app_handle: tauri::AppHandle<R>){
|
||||
let line_utf8 = String::from_utf8_lossy(&line).to_string();
|
||||
error!(Source = "Qdrant Server (stderr)"; "{line_utf8}");
|
||||
},
|
||||
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let is_available = QDRANT_STATUS.lock().unwrap().status == QdrantStatus::Available;
|
||||
let unavailable_reason = if is_available {
|
||||
"Qdrant server process stopped.".to_string()
|
||||
} else {
|
||||
"Qdrant server process stopped before it became available.".to_string()
|
||||
};
|
||||
set_qdrant_unavailable(unavailable_reason);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,6 +269,20 @@ pub fn stop_qdrant_server() {
|
||||
cleanup_qdrant();
|
||||
}
|
||||
|
||||
async fn wait_for_qdrant_startup(init_path: PathBuf) -> bool {
|
||||
let mut elapsed = Duration::ZERO;
|
||||
while elapsed < STARTUP_TIMEOUT {
|
||||
if init_path.exists() {
|
||||
return true;
|
||||
}
|
||||
|
||||
time::sleep(STARTUP_CHECK_INTERVAL).await;
|
||||
elapsed += STARTUP_CHECK_INTERVAL;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Create a temporary directory with TLS relevant files
|
||||
pub fn create_temp_tls_files(path: &PathBuf) -> Result<(PathBuf, PathBuf), Box<dyn Error>> {
|
||||
let cert = generate_certificate();
|
||||
@@ -278,13 +335,19 @@ pub fn cleanup_qdrant() {
|
||||
|
||||
fn set_qdrant_available() {
|
||||
let mut status = QDRANT_STATUS.lock().unwrap();
|
||||
status.is_available = true;
|
||||
status.status = QdrantStatus::Available;
|
||||
status.unavailable_reason = None;
|
||||
}
|
||||
|
||||
fn set_qdrant_starting() {
|
||||
let mut status = QDRANT_STATUS.lock().unwrap();
|
||||
status.status = QdrantStatus::Starting;
|
||||
status.unavailable_reason = None;
|
||||
}
|
||||
|
||||
fn set_qdrant_unavailable(reason: String) {
|
||||
let mut status = QDRANT_STATUS.lock().unwrap();
|
||||
status.is_available = false;
|
||||
status.status = QdrantStatus::Unavailable;
|
||||
status.unavailable_reason = Some(reason);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user