google_cloud_bigquery/write/arrow/
committed.rs1use super::base::BaseWriter;
16use crate::Result;
17use crate::model::{ArrowRecordBatch, ArrowSchema, FinalizeWriteStreamResponse};
18use crate::write::append_builder::AppendWithOffset;
19use crate::write::transport::Transport;
20use std::sync::Arc;
21
22#[derive(Debug)]
26pub struct CommittedWriter {
27 pub(crate) inner: BaseWriter,
28}
29
30impl CommittedWriter {
31 pub(crate) fn new(inner: Arc<Transport>, write_stream: String, schema: ArrowSchema) -> Self {
32 Self {
33 inner: BaseWriter::new(inner, write_stream, schema),
34 }
35 }
36
37 pub fn write_stream(&self) -> &str {
39 &self.inner.write_stream
40 }
41
42 pub fn append(&self, rows: ArrowRecordBatch) -> AppendWithOffset {
44 AppendWithOffset::new(
45 self.inner.runner.req_tx.clone(),
46 self.inner.append_request(rows),
47 )
48 }
49
50 pub async fn finalize(&self) -> Result<FinalizeWriteStreamResponse> {
52 self.inner.finalize().await
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::super::super::runner::tests::*;
59 use super::super::super::transport::tests::*;
60 use super::*;
61 use crate::error::AppendError;
62 use bigquery_grpc_mock::{MockBigQueryWrite, start};
63 use gaxi::grpc::tonic::Response as TonicResponse;
64 use tokio::sync::mpsc;
65
66 #[tokio::test]
67 async fn request_fields() -> anyhow::Result<()> {
68 let transport = Arc::new(test_transport("http://ignored:1".to_string()).await?);
69 let writer = CommittedWriter::new(transport, write_stream(), schema());
70 assert_eq!(writer.write_stream(), write_stream());
71
72 let b = writer.append(rows(1));
73 assert_eq!(b.req.write_stream, write_stream());
74 let data = b.req.arrow_rows().expect("arrow rows should be set");
75 let s = data.writer_schema.as_ref().expect("schema should be set");
76 assert_eq!(s.serialized_schema, "test");
77 let r = data.rows.as_ref().expect("rows should be set");
78 assert_eq!(r.serialized_record_batch, "1");
79
80 let b = writer.append(rows(2));
81 assert_eq!(b.req.write_stream, write_stream());
82 let data = b.req.arrow_rows().expect("arrow rows should be set");
83 let s = data.writer_schema.as_ref().expect("schema should be set");
84 assert_eq!(s.serialized_schema, "test");
85 let r = data.rows.as_ref().expect("rows should be set");
86 assert_eq!(r.serialized_record_batch, "2");
87
88 Ok(())
89 }
90
91 #[tokio::test]
92 async fn basic_success() -> anyhow::Result<()> {
93 let (response_tx, response_rx) = mpsc::channel(10);
94
95 let mut mock = MockBigQueryWrite::new();
96 mock.expect_append_rows()
97 .return_once(|_| Ok(TonicResponse::from(response_rx)));
98
99 mock.expect_finalize_write_stream().return_once(|_| {
100 Ok(TonicResponse::new(
101 bigquery_grpc_mock::google::cloud::bigquery::storage::v1::FinalizeWriteStreamResponse::default(),
102 ))
103 });
104
105 let (endpoint, _server) = start("0.0.0.0:0", mock).await?;
106 let transport = Arc::new(test_transport(endpoint).await?);
107
108 let writer = CommittedWriter::new(transport, write_stream(), schema());
109 assert_eq!(writer.write_stream(), write_stream());
110
111 response_tx.send(Ok(convert(&test_response(1)))).await?;
112 let resp = writer.append(rows(1)).send().await?;
113 assert_eq!(resp.offset, Some(1));
114
115 response_tx.send(Ok(convert(&test_response(2)))).await?;
116 let resp = writer.append(rows(2)).send().await?;
117 assert_eq!(resp.offset, Some(2));
118
119 response_tx.send(Ok(convert(&test_response(3)))).await?;
120 let resp = writer.append(rows(3)).send().await?;
121 assert_eq!(resp.offset, Some(3));
122
123 drop(response_tx);
124 let err = writer.append(rows(4)).send().await.expect_err("channel");
125 assert!(matches!(err, AppendError::UnexpectedEndOfStream));
126
127 writer.finalize().await?;
129
130 Ok(())
131 }
132
133 fn write_stream() -> String {
134 "projects/p/datasets/d/tables/t/streams/s".to_string()
135 }
136
137 fn schema() -> ArrowSchema {
138 ArrowSchema::new().set_serialized_schema("test")
139 }
140
141 fn rows(id: i64) -> ArrowRecordBatch {
142 ArrowRecordBatch::new().set_serialized_record_batch(id.to_string())
143 }
144}