@@ -29,16 +29,17 @@ use std::fs::OpenOptions;
29
29
use std:: io:: BufReader ;
30
30
use std:: sync:: Arc ;
31
31
use std:: sync:: Mutex ;
32
+ use std:: sync:: MutexGuard ;
32
33
use std:: sync:: RwLock ;
33
34
34
35
use crate :: metadata;
35
- use crate :: metadata:: STREAM_INFO ;
36
36
use crate :: option:: CONFIG ;
37
37
use crate :: response;
38
38
use crate :: storage:: ObjectStorage ;
39
39
use crate :: Error ;
40
40
41
41
type LocalWriter = Mutex < Option < StreamWriter < std:: fs:: File > > > ;
42
+ type LocalWriterGuard < ' a > = MutexGuard < ' a , Option < StreamWriter < std:: fs:: File > > > ;
42
43
43
44
lazy_static ! {
44
45
#[ derive( Default ) ]
@@ -47,102 +48,126 @@ lazy_static! {
47
48
48
49
impl STREAM_WRITERS {
49
50
// append to a existing stream
50
- fn append_to_local ( stream : & str , record : & RecordBatch ) -> Result < ( ) , ( ) > {
51
- let hashmap_guard = STREAM_WRITERS . read ( ) . unwrap ( ) ;
51
+ fn append_to_local ( stream : & str , record : & RecordBatch ) -> Result < ( ) , StreamWriterError > {
52
+ let hashmap_guard = STREAM_WRITERS
53
+ . read ( )
54
+ . map_err ( |_| StreamWriterError :: RwPoisioned ) ?;
55
+
52
56
match hashmap_guard. get ( stream) {
53
57
Some ( localwriter) => {
54
- let mut writer_guard = localwriter. lock ( ) . unwrap ( ) ;
58
+ let mut writer_guard = localwriter
59
+ . lock ( )
60
+ . map_err ( |_| StreamWriterError :: MutexPoisioned ) ?;
61
+
62
+ // if it's some writer then we write without dropping any lock
63
+ // hashmap cannot be brought mutably at any point until this finishes
55
64
if let Some ( ref mut writer) = * writer_guard {
56
- writer. write ( record) . map_err ( |_| ( ) ) ?;
65
+ writer. write ( record) . map_err ( StreamWriterError :: Writer ) ?;
57
66
} else {
58
- drop ( writer_guard) ;
59
- drop ( hashmap_guard) ;
60
- STREAM_WRITERS :: set_entry ( stream, record) . unwrap ( ) ;
67
+ // pass on this mutex to set entry so that it can be reused
68
+ // we have a guard for underlying entry thus
69
+ // hashmap must not be availible as mutable to any other thread
70
+ STREAM_WRITERS :: set_entry ( writer_guard, stream, record) ?;
61
71
}
62
72
}
73
+ // entry is not present thus we create it
63
74
None => {
75
+ // this requires mutable borrow of the map so we drop this read lock and wait for write lock
64
76
drop ( hashmap_guard) ;
65
- STREAM_WRITERS :: create_entry ( stream. to_string ( ) , record) . unwrap ( ) ;
77
+ STREAM_WRITERS :: create_entry ( stream. to_string ( ) , record) ? ;
66
78
}
67
79
} ;
68
80
Ok ( ( ) )
69
81
}
70
82
71
83
// create a new entry with new stream_writer
72
- // todo: error type
73
84
// Only create entry for valid streams
74
- fn create_entry ( stream : String , record : & RecordBatch ) -> Result < ( ) , ( ) > {
75
- let mut hashmap_guard = STREAM_WRITERS . write ( ) . unwrap ( ) ;
76
-
77
- if STREAM_INFO . schema ( & stream) . is_err ( ) {
78
- return Err ( ( ) ) ;
79
- }
85
+ fn create_entry ( stream : String , record : & RecordBatch ) -> Result < ( ) , StreamWriterError > {
86
+ let mut hashmap_guard = STREAM_WRITERS
87
+ . write ( )
88
+ . map_err ( |_| StreamWriterError :: RwPoisioned ) ?;
80
89
81
90
let file = OpenOptions :: new ( )
82
91
. append ( true )
83
92
. create_new ( true )
84
93
. open ( data_file_path ( & stream) )
85
- . map_err ( |_| ( ) ) ?;
94
+ . map_err ( StreamWriterError :: Io ) ?;
86
95
87
- let mut stream_writer = StreamWriter :: try_new ( file, & record. schema ( ) ) . map_err ( |_| ( ) ) ?;
88
- stream_writer. write ( record) . map_err ( |_| ( ) ) ?;
96
+ let mut stream_writer = StreamWriter :: try_new ( file, & record. schema ( ) )
97
+ . expect ( "File and RecordBatch both are checked" ) ;
98
+
99
+ stream_writer
100
+ . write ( record)
101
+ . map_err ( StreamWriterError :: Writer ) ?;
89
102
90
103
hashmap_guard. insert ( stream, Mutex :: new ( Some ( stream_writer) ) ) ;
91
104
92
105
Ok ( ( ) )
93
106
}
94
107
95
108
// Deleting a logstream requires that metadata is deleted first
96
- pub fn delete_entry ( stream : & str ) -> Result < ( ) , ( ) > {
97
- let mut hashmap_guard = STREAM_WRITERS . write ( ) . unwrap ( ) ;
98
-
99
- if STREAM_INFO . schema ( stream) . is_ok ( ) {
100
- return Err ( ( ) ) ;
101
- }
109
+ pub fn delete_entry ( stream : & str ) -> Result < ( ) , StreamWriterError > {
110
+ let mut hashmap_guard = STREAM_WRITERS
111
+ . write ( )
112
+ . map_err ( |_| StreamWriterError :: RwPoisioned ) ?;
102
113
103
114
hashmap_guard. remove ( stream) ;
104
115
105
116
Ok ( ( ) )
106
117
}
107
118
108
- fn set_entry ( stream : & str , record : & RecordBatch ) -> Result < ( ) , ( ) > {
119
+ fn set_entry (
120
+ mut writer_guard : LocalWriterGuard ,
121
+ stream : & str ,
122
+ record : & RecordBatch ,
123
+ ) -> Result < ( ) , StreamWriterError > {
109
124
let file = OpenOptions :: new ( )
110
125
. append ( true )
111
126
. create_new ( true )
112
127
. open ( data_file_path ( stream) )
113
- . map_err ( |_| ( ) ) ?;
128
+ . map_err ( StreamWriterError :: Io ) ?;
114
129
115
- let mut stream_writer = StreamWriter :: try_new ( file, & record. schema ( ) ) . map_err ( |_| ( ) ) ? ;
116
- stream_writer . write ( record ) . map_err ( |_| ( ) ) ? ;
130
+ let mut stream_writer = StreamWriter :: try_new ( file, & record. schema ( ) )
131
+ . expect ( "File and RecordBatch both are checked" ) ;
117
132
118
- STREAM_WRITERS
119
- . read ( )
120
- . expect ( "Current Thread should not hold any lock" )
121
- . get ( stream)
122
- . expect ( "set entry is only called on valid entries" )
123
- . lock ( )
124
- . expect ( "Poisioning is not handled yet" )
125
- . replace ( stream_writer) ; // replace the stream writer behind this mutex
133
+ stream_writer
134
+ . write ( record)
135
+ . map_err ( StreamWriterError :: Writer ) ?;
136
+
137
+ writer_guard. replace ( stream_writer) ; // replace the stream writer behind this mutex
126
138
127
139
Ok ( ( ) )
128
140
}
129
141
130
142
// Unset the entry so that
131
- pub fn unset_entry ( stream : & str ) {
132
- let guard = STREAM_WRITERS . read ( ) . unwrap ( ) ;
143
+ pub fn unset_entry ( stream : & str ) -> Result < ( ) , StreamWriterError > {
144
+ let guard = STREAM_WRITERS
145
+ . read ( )
146
+ . map_err ( |_| StreamWriterError :: RwPoisioned ) ?;
133
147
let stream_writer = match guard. get ( stream) {
134
148
Some ( writer) => writer,
135
- None => return ,
149
+ None => return Ok ( ( ) ) ,
136
150
} ;
137
151
stream_writer
138
152
. lock ( )
139
- . expect ( "Poisioning is not handled yet" )
153
+ . map_err ( |_| StreamWriterError :: MutexPoisioned ) ?
140
154
. take ( ) ;
155
+
156
+ Ok ( ( ) )
141
157
}
142
158
}
143
159
144
160
#[ derive( Debug , thiserror:: Error ) ]
145
- enum StreamWriterError { }
161
+ pub enum StreamWriterError {
162
+ #[ error( "Arrow writer failed: {0}" ) ]
163
+ Writer ( arrow:: error:: ArrowError ) ,
164
+ #[ error( "Io Error when creating new file: {0}" ) ]
165
+ Io ( std:: io:: Error ) ,
166
+ #[ error( "RwLock was poisioned" ) ]
167
+ RwPoisioned ,
168
+ #[ error( "Mutex was poisioned" ) ]
169
+ MutexPoisioned ,
170
+ }
146
171
147
172
fn data_file_path ( stream_name : & str ) -> String {
148
173
format ! (
@@ -253,7 +278,7 @@ impl Event {
253
278
let rb = event. next ( ) ?. ok_or ( Error :: MissingRecord ) ?;
254
279
let stream_name = & self . stream_name ;
255
280
256
- STREAM_WRITERS :: append_to_local ( stream_name, & rb) . map_err ( |_| Error :: MissingRecord ) ? ;
281
+ STREAM_WRITERS :: append_to_local ( stream_name, & rb) . unwrap ( ) ;
257
282
258
283
Ok ( 0 )
259
284
}
0 commit comments