@@ -5,7 +5,6 @@ use axum_client_ip::InsecureClientIp;
5
5
use log:: * ;
6
6
use serde:: { Deserialize , Serialize } ;
7
7
use serde_json:: json;
8
- use std:: sync:: Arc ;
9
8
use std:: time:: Duration ;
10
9
use tracing:: field:: Field ;
11
10
use tracing:: span:: { Attributes , Id , Record , Span } ;
@@ -98,80 +97,66 @@ pub(super) fn sqlite_logger_on_response(
98
97
) ;
99
98
}
100
99
101
- struct SqliteLogLayerState {
102
- conn : trailbase_sqlite:: Connection ,
103
- sender : crossbeam_channel:: Sender < LogFieldStorage > ,
104
- receiver : crossbeam_channel:: Receiver < LogFieldStorage > ,
105
- }
106
-
107
100
pub struct SqliteLogLayer {
108
- state : Arc < SqliteLogLayerState > ,
101
+ sender : tokio :: sync :: mpsc :: UnboundedSender < Box < LogFieldStorage > > ,
109
102
}
110
103
111
104
impl SqliteLogLayer {
112
105
pub fn new ( state : & AppState ) -> Self {
106
+ // NOTE: We're boxing the channel contents to lower the growth rate of back-stopped unbound
107
+ // channels. The underlying container doesn't seem to every shrink :/.
108
+ //
113
109
// TODO: should we use a bounded receiver to create back-pressure?
114
- let ( sender, receiver) = crossbeam_channel:: unbounded ( ) ;
115
- return SqliteLogLayer {
116
- state : Arc :: new ( SqliteLogLayerState {
117
- conn : state. logs_conn ( ) . clone ( ) ,
118
- sender,
119
- receiver,
120
- } ) ,
121
- } ;
122
- }
123
-
124
- // The writer runs in a separate Task in the background and receives Logs via a channel, which it
125
- // then writes to Sqlite.
126
- fn write_log ( & self , log : LogFieldStorage ) -> Result < ( ) , trailbase_sqlite:: Error > {
127
- let state = self . state . clone ( ) ;
128
- state. sender . send ( log) . expect ( BUG_TEXT ) ;
110
+ let ( sender, mut receiver) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
129
111
112
+ let conn = state. logs_conn ( ) . clone ( ) ;
130
113
let rt = tokio:: runtime:: Handle :: current ( ) ;
131
114
rt. spawn ( async move {
132
- tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
115
+ const LIMIT : usize = 128 ;
116
+ let mut buffer = Vec :: < Box < LogFieldStorage > > :: with_capacity ( LIMIT ) ;
117
+
118
+ while receiver. recv_many ( & mut buffer, LIMIT ) . await > 0 {
119
+ let logs = std:: mem:: take ( & mut buffer) ;
120
+
121
+ let result = conn
122
+ . call ( move |conn| {
123
+ if logs. len ( ) > 1 {
124
+ let tx = conn. transaction ( ) ?;
125
+ for log in logs {
126
+ SqliteLogLayer :: insert_log ( & tx, log) ?;
127
+ }
128
+ tx. commit ( ) ?;
129
+ } else {
130
+ for log in logs {
131
+ Self :: insert_log ( conn, log) ?
132
+ }
133
+ }
133
134
134
- // Work stealing.
135
- let logs = state. receiver . try_iter ( ) . take ( 128 ) . collect :: < Vec < _ > > ( ) ;
136
- if logs. is_empty ( ) {
137
- return ;
138
- }
135
+ Ok ( ( ) )
136
+ } )
137
+ . await ;
139
138
140
- let result = state. conn . call_and_forget ( move |conn| {
141
- if logs. len ( ) > 1 {
142
- fn commit (
143
- conn : & mut rusqlite:: Connection ,
144
- logs : Vec < LogFieldStorage > ,
145
- ) -> Result < ( ) , rusqlite:: Error > {
146
- let tx = conn. transaction ( ) ?;
147
- for log in logs {
148
- SqliteLogLayer :: insert_log ( & tx, log) ?;
149
- }
150
- return tx. commit ( ) ;
151
- }
152
-
153
- if let Err ( err) = commit ( conn, logs) {
154
- log:: warn!( "log insert failed: {err}" ) ;
155
- }
156
- } else {
157
- for log in logs {
158
- if let Err ( err) = Self :: insert_log ( conn, log) {
159
- log:: warn!( "log insert failed: {err}" ) ;
160
- }
161
- }
139
+ if let Err ( err) = result {
140
+ warn ! ( "Failed to send logs: {err}" ) ;
162
141
}
163
- } ) ;
164
-
165
- if let Err ( err) = result {
166
- error ! ( "{err}" ) ;
167
142
}
168
143
} ) ;
169
144
170
- return Ok ( ( ) ) ;
145
+ return SqliteLogLayer { sender } ;
171
146
}
172
147
148
+ // The writer runs in a separate Task in the background and receives Logs via a channel, which it
149
+ // then writes to Sqlite.
173
150
#[ inline]
174
- fn insert_log ( conn : & rusqlite:: Connection , log : LogFieldStorage ) -> Result < ( ) , rusqlite:: Error > {
151
+ fn write_log ( & self , log : LogFieldStorage ) {
152
+ self . sender . send ( Box :: new ( log) ) . expect ( BUG_TEXT ) ;
153
+ }
154
+
155
+ #[ inline]
156
+ fn insert_log (
157
+ conn : & rusqlite:: Connection ,
158
+ log : Box < LogFieldStorage > ,
159
+ ) -> Result < ( ) , rusqlite:: Error > {
175
160
lazy_static:: lazy_static! {
176
161
static ref QUERY : String = indoc:: formatdoc! { "
177
162
INSERT INTO
@@ -233,9 +218,7 @@ where
233
218
234
219
storage. level = level_to_int ( event. metadata ( ) . level ( ) ) ;
235
220
236
- if let Err ( err) = self . write_log ( storage) {
237
- warn ! ( "Failed to send to logs to writer: {err}" ) ;
238
- }
221
+ self . write_log ( storage) ;
239
222
}
240
223
}
241
224
}
0 commit comments