Please enable JavaScript to view the comments powered by Disqus.BigQuery Springboot 연동, Multi-result 와 Stored procedure 사용하기
Search

BigQuery Springboot 연동, Multi-result 와 Stored procedure 사용하기

태그
BigQuery
spring boot
Stored procedure
Multi-result
공개여부
작성일자
2022/11/18
Springboot 와 Google Cloud 의 BigQuery를 연동하여 API를 개발해야 한다. 빅데이터팀이 정의한 multi-result 를 반환하는 Stored proecdure를 spring boot 와 연결해본다.
이와 관련된 글을 찾기가 어려워서 직접 trouble shooting 을 완료하고 다른 개발자들이 내가 소비한 시간을 동일하게 소비하지 않기를 바란다.
개요

상황

지표 API를 개발하게 되었다. 데이터는 BigQuery 에 존재한다.
직접 쿼리를 실행하자니 big query의 쿼리를 직접 BE팀에서 실행하는데는 시간이 부족하다.
한번의 stored procedure 를 호출하여 여러개의 결과를 반환하는 stored procedure 를 제공받는다.
이 Stored procedure를 Spring application 에서 호출한다.
Kotlin, Spring boot 를 사용한다.

선택권

1.
Google cloud 의 client 를 사용한다. 링크
2.
Spring boot starter 에 있는 cloud integration 을 사용한다.

결정한 부분

결론부터 말하자면 설정을 편리하게 하기 위해 spring boot starter 패키지를 사용했다. 설정 자체는 별거 아니지만 이왕 spring boot 를 사용하는데 이 편이 더 편리하겠다고 생각했다.

사전작업

API를 사용하도록 big query 의 Enable API 설정을 on 해야한다.
이후 부분은 위 문서에 잘 나와있으니 service account 생성과 관련된 것은 위 문서를 참고하고, service key를 준비 해둔다.

Gradle 에 추가할 문장

extra["springCloudGcpVersion"] = "3.4.0" extra["springCloudVersion"] = "2021.0.5" implementation("com.google.cloud:spring-cloud-gcp-starter") implementation("com.google.cloud:spring-cloud-gcp-starter-bigquery") dependencyManagement { imports { mavenBom("com.google.cloud:spring-cloud-gcp-dependencies:${property("springCloudGcpVersion")}") mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}") } }
Kotlin
복사
Gradle 에 위와 같이 추가하여 Big Query를 사용할 수 있도록 설정한다.
이와 같은 설정을 해두면 Spring boot 의 auto configuration 을 사용할 수 있다.

Properties 에 추가할 내용

위 문서에 포함된 Configuration 을 application.properties 혹은 application.yml 에 추가한다.
spring.cloud.gcp.bigquery.datasetName
BigQuery의 dataset 과 BigQueryTemplate, BigQueryFileMessageHandler 의 scope 범위에 해당할 dataset 이름을 할당한다.
반드시 필요한 값이며 default 는 없다.
spring.cloud.gcp.bigquery.enabled
Auto configuration 을 사용할지 말지를 결정한다.
별도로 기입하지 않는다면 활성화된다.
spring.cloud.gcp.bigquery.credentials.location
Credential file 의 위치를 명시할 곳이다.
이 글에서는 사용하지 않는다. base64 로 인코딩 하여 하단의 field 에 할당할 것이다.
Github secret 을 사용하여 pod 로 배포할 것을 고려하면 mount 등을 하기 귀찮다고 생각했다.
spring.cloud.gcp.bigquery.jsonWriterBatchSize
BigQueryJosnDataWriter 를 통해 데이터를 저장하는 API를 호출할 때 사용할 batch size 이다.
내 요구사항에선 읽기, select 호출만 필요하기 때문에 따로 사용하지 않는다.
spring.cloud.gcp.bigquery.threadPoolSize
BigQueryTemplate 을 사용할 때 thread pool 의 크기를 명시한다.
Default는 4이고, required 가 아니다.
내 요구사항에선 BigQueryTemplate 이 필요하지 않을것으로 예상된다.

Configuration

이와같이 사용할 수 있다.

Key file 을 base64로 encoding 하기

cat /path/to/key/file | base64
Shell
복사
Key file 을 base64로 인코딩한다.
만약 file 을 사용한다면 필요하지 않은 작업이다.
우리의 환경은 K8S 로 배포하고 docker file 을 직접 빌드해서 AWS ECR 에 올려야 하는데 이 과정보단 github flow 를 통해서 Docker file 빌드시에 주입하는것이 편리하다고 생각했다.
이제 select 만 한다면 모든 설정은 완료되었다.

Select Query 혹은 stored procedure 호출하기

fun executeCall(query: String) { val queryConfig = QueryJobConfiguration .newBuilder(query) .build() val tableResult = bigQuery.query(queryConfig) for (row in tableResult.iterateAll()) { println("---------------start-----------------") for (value in row) { println(value) } } }
Kotlin
복사
간단한 select Query 를 호출하는 함수
query 에 select 쿼리를 입력하면 BigQuery 로 쿼리문을 전송하여 결과를 받아온다.
ResultSet 의 결과는 tableResult 로 들어오게 된다.
그 결과 안엔 field 가 하나하나 있는데 이를 Entity 로 쉽게 매핑하는 방법은 아직 찾기 못했다.

Multi-result set

Result 가 여러개이다. 이건 2번째 결과이다.
이 결과는 3번째 결과이다.
이 Stored precedure 는 4개의 결과 집합을 반환한다.
문제는 위의 함수로는 가장 마지막 결과만 확인할 수 있다. 그래서 1, 2, 3번째의 결과를 JobId 를 통해 찾을 필요가 있다.
그래서 JobId 를 개발자가 직접 관리(만들어야)해야 한다.
나중에 Key generator 를 사용하겠지만, 현재는 간단하게 UUID를 사용하여 JobId 를 만들어 다음과 같이 사용한다.
@Service class CallProcedure( private val bigQuery: BigQuery, ) { fun executeCall(query: String) { val queryConfig = QueryJobConfiguration .newBuilder(query) .setUseLegacySql(false) // 이 설정이 stored procedure 를 호출하게 만든다 .build() val jobId = JobId.of(UUID.randomUUID().toString()).also { bigQuery.query(queryConfig, it) } // multi-result val children = bigQuery.listJobs(JobListOption.parentJobId(jobId.job)) for (child in children.iterateAll()) { println("----------??----------") println(child.getQueryResults()) } } }
Kotlin
복사
Job ID 를 만들고, job id로 각각의 결과 집합을 찾는다.
결과 집합은 parentJobId 를 통해서 찾아온다.
children 은 결과집합이 여러개일 경우 iterateAll 함수를 통해 가져올 수 있다.

결과

기존의 spring application 은 JPA repository 를 많이 사용했다.
이와 비슷한 컨셉으로 repository 를 정의할 예정이다.
이를 통해서 Entity 를 만들고 결과 속성값을 할당하려고 한다.
자동으로 생성되는 BigQuery 를 DI로 주입받으면 바로 select 를 할 수 있다.
@SpringBootTest internal class BigqueryStatisticsRepositoryTest { @Autowired private lateinit var bigquery: BigQuery @Test fun `call procedure`() { executeCall(""" CALL dataset.procedure(param1, param2, ...) """.trimIndent()) } }
Kotlin
복사
위와 같이 코드를 작성하여 결과를 확인할 수 있다.
println() 로 나온 결과를 찍거나, break point 를 통해서 주어진 상황에 알맞게 결과를 deserialize 하면 요구사항을 충분히 충족할 수 있을 것이다.

참조한 문서

이 외에 write operation 의 경우는 모두 spring cloud big query 의 문서를 통해 확인할 수 있다.
참조한 문서들은 다음과 같다.

샘플코드

이와 관련된 기능을 찾다보면 archive 된 github repo도 존재한다.
주의해야 할 것은 이 버전은 오래된 것이기 때문에 현재 사용할 이유가 없다.