Skip to content

Commit c0ce0e5

Browse files
authored
Remove async (#1671)
* Remove async - make MemWriter optional - remove unnecessary async * separate ingestion runtime * bump MSRV * make MemWriter optional * docker image versions bump
1 parent a5cc347 commit c0ce0e5

11 files changed

Lines changed: 237 additions & 131 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "parseable"
33
version = "2.8.0"
44
authors = ["Parseable Team <hi@parseable.com>"]
55
edition = "2024"
6-
rust-version = "1.88.0"
6+
rust-version = "1.91.0"
77
categories = ["logs", "observability", "metrics", "traces"]
88
build = "build.rs"
99

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1515

1616
# build stage
17-
FROM rust:1.93.0-bookworm AS builder
17+
FROM rust:1.96.0-bookworm AS builder
1818

1919
LABEL org.opencontainers.image.title="Parseable"
2020
LABEL maintainer="Parseable Team <hi@parseable.io>"

Dockerfile.debug

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1515

1616
# build stage
17-
FROM docker.io/rust:1.93.0-bookworm AS builder
17+
FROM docker.io/rust:1.96.0-bookworm AS builder
1818

1919
LABEL org.opencontainers.image.title="Parseable"
2020
LABEL maintainer="Parseable Team <hi@parseable.io>"

Dockerfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1515

1616
# build stage
17-
FROM rust:1.93.0-bookworm AS builder
17+
FROM rust:1.96.0-bookworm AS builder
1818

1919
LABEL org.opencontainers.image.title="Parseable"
2020
LABEL maintainer="Parseable Team <hi@parseable.io>"

Dockerfile.kafka

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1515

1616
# build stage
17-
FROM rust:1.93.0-bookworm AS builder
17+
FROM rust:1.96.0-bookworm AS builder
1818

1919
LABEL org.opencontainers.image.title="Parseable"
2020
LABEL maintainer="Parseable Team <hi@parseable.io>"

src/handlers/http/ingest.rs

Lines changed: 36 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,19 @@ use actix_web::{HttpRequest, HttpResponse, http::header::ContentType};
2424
use arrow_array::RecordBatch;
2525
use bytes::Bytes;
2626
use chrono::Utc;
27+
use tokio::sync::oneshot;
2728
use tracing::error;
2829

2930
use crate::event::error::EventError;
3031
use crate::event::format::known_schema::{self, KNOWN_SCHEMA_LIST};
3132
use crate::event::format::{self, EventFormat, LogSource, LogSourceEntry};
3233
use crate::event::{self, FORMAT_KEY, USER_AGENT_KEY};
33-
use crate::handlers::http::modal::utils::ingest_utils::validate_stream_for_ingestion;
34+
use crate::handlers::http::modal::utils::ingest_utils::{
35+
ingest_helper, process_otel_content, validate_stream_for_ingestion,
36+
};
3437
use crate::handlers::{
35-
CONTENT_TYPE_JSON, CONTENT_TYPE_PROTOBUF, DatasetTag, EXTRACT_LOG_KEY, LOG_SOURCE_KEY,
36-
STREAM_NAME_HEADER_KEY, TELEMETRY_TYPE_KEY, TelemetryType,
38+
DatasetTag, EXTRACT_LOG_KEY, LOG_SOURCE_KEY, STREAM_NAME_HEADER_KEY, TELEMETRY_TYPE_KEY,
39+
TelemetryType,
3740
};
3841
use crate::metadata::SchemaVersion;
3942
use crate::metastore::MetastoreError;
@@ -81,10 +84,11 @@ pub async fn ingest(
8184
.and_then(|h| h.to_str().ok())
8285
.map_or(TelemetryType::default(), TelemetryType::from);
8386

84-
let extract_log = req
85-
.headers()
86-
.get(EXTRACT_LOG_KEY)
87-
.and_then(|h| h.to_str().ok());
87+
let extract_log = req.headers().get(EXTRACT_LOG_KEY).and_then(|h| {
88+
h.to_str()
89+
.ok()
90+
.map_or_else(|| None, |h| Some(h.to_string()))
91+
});
8892

8993
if matches!(
9094
log_source,
@@ -97,15 +101,14 @@ pub async fn ingest(
97101
}
98102

99103
let mut p_custom_fields = get_custom_fields_from_header(&req);
100-
101104
let mut json = json.into_inner();
102105

103106
let fields = match &log_source {
104107
LogSource::Custom(src) => KNOWN_SCHEMA_LIST.extract_from_inline_log(
105108
&mut json,
106109
&mut p_custom_fields,
107110
src,
108-
extract_log,
111+
extract_log.as_deref(),
109112
)?,
110113
_ => HashSet::new(),
111114
};
@@ -129,12 +132,13 @@ pub async fn ingest(
129132
e
130133
})?;
131134

132-
//if stream exists, fetch the stream log source
133-
//return error if the stream log source is otel traces or otel metrics or otel logs
135+
// if stream exists, fetch the stream log source
136+
// return error if the stream log source is otel traces or otel metrics
134137
validate_stream_for_ingestion(&stream_name, &log_source, &tenant_id).map_err(|e| {
135138
error!("Ingestion failed for stream {stream_name}: {e}");
136139
e
137140
})?;
141+
let stream = PARSEABLE.get_stream(&stream_name, &tenant_id)?;
138142

139143
PARSEABLE
140144
.add_update_log_source(&stream_name, log_source_entry, &tenant_id)
@@ -144,22 +148,27 @@ pub async fn ingest(
144148
e
145149
})?;
146150

147-
if let Err(e) = flatten_and_push_logs(
148-
json,
149-
&stream_name,
150-
&log_source,
151-
&p_custom_fields,
152-
None,
153-
telemetry_type,
154-
&tenant_id,
155-
)
156-
.await
157-
{
158-
error!("Ingestion failed for stream {stream_name}: {e}");
159-
return Err(e);
160-
}
151+
let time_partition = stream.get_time_partition();
161152

162-
Ok(HttpResponse::Ok().finish())
153+
let (s, r) = oneshot::channel();
154+
rayon::spawn(move || {
155+
let res = ingest_helper(
156+
stream_name,
157+
tenant_id,
158+
log_source,
159+
telemetry_type,
160+
p_custom_fields,
161+
json,
162+
time_partition,
163+
);
164+
let _ = s.send(res);
165+
});
166+
167+
if let Err(e) = r.await.map_err(|e| PostError::CustomError(e.to_string()))? {
168+
Err(e)
169+
} else {
170+
Ok(HttpResponse::Ok().finish())
171+
}
163172
}
164173

165174
pub async fn ingest_internal_stream(
@@ -285,76 +294,6 @@ pub async fn setup_otel_stream(
285294
Ok((stream_name, log_source, log_source_entry, time_partition))
286295
}
287296

288-
// Common content processing for OTEL ingestion
289-
async fn process_otel_content(
290-
req: &HttpRequest,
291-
body: web::Bytes,
292-
stream_name: &str,
293-
log_source: &LogSource,
294-
telemetry_type: TelemetryType,
295-
) -> Result<(), PostError> {
296-
let p_custom_fields = get_custom_fields_from_header(req);
297-
298-
match req
299-
.headers()
300-
.get("Content-Type")
301-
.and_then(|h| h.to_str().ok())
302-
{
303-
Some(content_type) => {
304-
let tenant_id = get_tenant_id_from_request(req);
305-
if content_type == CONTENT_TYPE_JSON {
306-
let json: serde_json::Value = match serde_json::from_slice(&body) {
307-
Ok(v) => v,
308-
Err(e) => {
309-
error!(
310-
"Ingestion failed for stream {stream_name}: malformed JSON in request body"
311-
);
312-
return Err(PostError::SerdeError(e));
313-
}
314-
};
315-
if let Err(e) = flatten_and_push_logs(
316-
json,
317-
stream_name,
318-
log_source,
319-
&p_custom_fields,
320-
None,
321-
telemetry_type,
322-
&tenant_id,
323-
)
324-
.await
325-
{
326-
error!("Ingestion failed for stream {stream_name}: {e}");
327-
return Err(e);
328-
}
329-
} else if content_type == CONTENT_TYPE_PROTOBUF {
330-
error!(
331-
"Ingestion failed for stream {stream_name}: Protobuf ingestion is not supported in Parseable OSS"
332-
);
333-
return Err(PostError::Invalid(anyhow::anyhow!(
334-
"Ingestion failed for stream {stream_name}: Protobuf ingestion is not supported in Parseable OSS"
335-
)));
336-
} else {
337-
error!(
338-
"Ingestion failed for stream {stream_name}: Unsupported Content-Type: {content_type}. Expected application/json or application/x-protobuf"
339-
);
340-
return Err(PostError::Invalid(anyhow::anyhow!(
341-
"Ingestion failed for stream {stream_name}: Unsupported Content-Type: {content_type}. Expected application/json or application/x-protobuf"
342-
)));
343-
}
344-
}
345-
None => {
346-
error!(
347-
"Ingestion failed for stream {stream_name}: Missing Content-Type header. Expected application/json or application/x-protobuf"
348-
);
349-
return Err(PostError::Invalid(anyhow::anyhow!(
350-
"Ingestion failed for stream {stream_name}: Missing Content-Type header. Expected application/json or application/x-protobuf"
351-
)));
352-
}
353-
}
354-
355-
Ok(())
356-
}
357-
358297
// Handler for POST /v1/logs to ingest OTEL logs
359298
// ingests events by extracting stream name from header
360299
// creates if stream does not exist
@@ -519,9 +458,7 @@ pub async fn post_event(
519458
None,
520459
TelemetryType::Logs,
521460
&tenant_id,
522-
)
523-
.await
524-
{
461+
) {
525462
error!("Ingestion failed for stream {stream_name}: {e}");
526463
return Err(e);
527464
}

src/handlers/http/kinesis.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ struct Data {
5959
// "requestId": "b858288a-f5d8-4181-a746-3f3dd716be8a",
6060
// "timestamp": "1704964113659"
6161
// }
62-
pub async fn flatten_kinesis_logs(message: Message) -> Result<Vec<Value>, anyhow::Error> {
62+
pub fn flatten_kinesis_logs(message: Message) -> Result<Vec<Value>, anyhow::Error> {
6363
let mut vec_kinesis_json = Vec::new();
6464

6565
for record in message.records.iter() {

0 commit comments

Comments
 (0)