Introducing Experimental Support for Stored Programs in JS in Percona Server for MySQL

TL;DR
Percona Server for MySQL 에서 이제 JS 언어로 stored programs 을 실험적으로 지원합니다.
Oracle 엔터프라이즈/클라우드 전용 기능에 대한 무료 오픈소스 대안으로 사용자들은 보다 현대적이고 편리하며, 종종 더 친숙한 언어로 stored programs 을 작성할 수 있도록 합니다.
이 기능은 아직 활발히 개발 중이며, 이에 대한 여러분의 피드백을 매우 환영합니다!
바이너리 패키지는 Percona 의 실험적 저장소에서, 소스 코드는 GitHub 에서 확인할 수 있습니다.
Why we’re adding JS stored program support
작년에 Oracle 은 MySQL 서버에 저장된 JavaScript(JS) 프로그램에 대한 지원을 도입했습니다(이 기능은 2024년 여름에 MySQL Server 9.0 Innovation Release 에 출시된 이후 사용 가능했습니다).
이 기능은 사용자들이 기본 MySQL Stored Routine language(버전 5.0부터 사용 가능) 보다 더 현대적이고 편리하며, 아마도 더 익숙한 언어로 stored programs 을 작성할 수 있도록 합니다.
CPU 바운드 코드의 경우, JavaScript 프로그램은 더 나은 성능을 제공할 것입니다.
안타깝게도, Oracle 의 새로운 기능에는 한 가지 큰 문제가 있습니다.
이는 무료 오픈소스 MySQL 커뮤니티 버전에서는 사용할 수 없으며, MySQL 엔터프라이즈 제품 또는 Oracle 클라우드 서비스의 일부로만 제공됩니다.
이는 예를 들어 production(운영) 환경에서 무료로 사용할 수 없다는 것을 의미합니다.
Percona 는 오픈소스에 전념하고 있기 때문에, 우리는 JavaScript Stored Programs 에 대한 무료 오픈소스 대안을 제공하는 것이 좋은 생각이라고 결정했습니다.
따라서 우리는 Percona Server for MySQL 에서 JS Stored Programs 지원 작업을 시작했습니다.
Oracle의 구현 방식과 마찬가지로, Percona Server for MySQL 은 JS 로 작성된 Stored Programs 지원을 로드 가능한 컴포넌트로 구현합니다.
그러나 Oracle이 JavaScript 코드 실행을 위해 GraalVM 엔진을 사용하는 것과 달리, Percona 는 V8 엔진을 사용하기로 결정했습니다.
또한, Oracle 과는 달리 Percona 는 Percona Server for MySQL 의 8.4 시리즈(즉, LTS 시리즈)를 목표로 하고 있습니다.
현재 시점에서 Percona Server for MySQL 에서 이 기능은 아직 본격적인 실사용 단계에는 이르지 않았을 수 있지만, 이제 충분히 살펴보고 실험해볼 만한 단계에 도달했다고 생각합니다!
여러분의 피드백을 진심으로 기다리고 있습니다!
Where to get it: Binaries and source
이 기능이 활성화된 Percona Server for MySQL 의 바이너리 패키지를 Percona 의 실험용 저장소에서 제공합니다.
해당 저장소에 접근하는 방법은 다음 링크를 참고하세요: https://docs.percona.com/percona-software-repositories/index.html
특히, JS stored programs 지원이 포함된 서버 패키지를 설치하려면 percona-release 도구를 사용하여 “ps-84-lts” 저장소의 “experimental” 컴포넌트를 활성화해야 합니다.
$ sudo percona-release enable ps-84-lts experimental |
그 후에 표준 패키지 관리자를 사용하여 Percona Server for MySQL 버전 8.4.5-5 패키지를 설치할 수 있습니다.
이 기능의 소스 코드는 GitHub 에 별도 브랜치(https://github.com/percona/percona-server/tree/js-lang)에서도 제공됩니다.
이를 통해 JS stored programs 기능이 포함된 Percona Server for MySQL 을 직접 빌드할 수도 있습니다.
자세한 내용은 다음 문서를 참조하세요: https://github.com/percona/percona-server/blob/js-lang/components/js_lang/README.md
이 경우 가장 복잡한 부분은, Percona 코드와 호환되는 정확한 파라미터로 V8 엔진의 올바른 버전을 빌드하는 작업입니다.
Step-by-step: Getting started with JS stored programs
JS 지원이 가능한 Percona Server for MySQL 버전을 설치하고 시작한 후(예: experimental repository), 가장 먼저 해야 할 일은 다음을 사용하여 JS language component 를 설치하는 것입니다:
INSTALL COMPONENT 'file://component_js_lang'; |
또한 JS stored programs 을 생성할 사용자에게 (표준 CREATE ROUTINE 권한 외에) 새로운 전역 동적 CREATE_JS_ROUTINE 권한을 부여해야 합니다. 예를 들면 다음과 같습니다:
GRANT CREATE_JS_ROUTINE ON *.* TO root@localhost; |
그 후, 해당 사용자 중 한 명으로 접속하여 LANGUAGE JS 절이 포함된 CREATE PROCEDURE 또는 CREATE FUNCTION 문을 사용하여 JS 언어로 stored programs 을 생성할 수 있습니다. 예를 들면 다음과 같습니다:
CREATE FUNCTION fact(n INT) RETURNS INT LANGUAGE JS AS $$let result = 1;while (n > 1) {result *= n;n--;}return result;$$; |
함수 본문을 구분하는 $$ 에 주목하세요.
이는 JS 코드 내에서 일반적으로 SQL 문을 구분하는 데 사용되는 세미콜론(;) 문자가 사용되기 때문에 필요합니다.
최신 버전의 mysql 클라이언트는 이러한 $$ 구문을 별도의 조치 없이 잘 처리할 수 있도록 업데이트되어 있습니다.
그러나 이전 클라이언트 버전에서는 혼동을 피하기 위해 세미콜론(;)이 아닌 다른 문자를 구분자로 할당해야 할 수도 있습니다.
이는 클라이언트 전용 DELIMITER 문을 사용하여 수행할 수 있습니다.
JS 저장 루틴이 생성되면, 일반 SQL 루틴처럼 동일한 방식으로 호출할 수 있습니다.
mysql> SELECT fact(5);+---------+| fact(5) |+---------+| 120 |+---------+1 row in set (0.00 sec) |
이는 `INFORMATION_SCHEMA.ROUTINES` 테이블에 표시되며, 필요한 경우 삭제할 수 있습니다:
mysql> SELECT routine_schema AS s, routine_name AS n, routine_definition AS def, external_language AS l FROM INFORMATION_SCHEMA.ROUTINES WHERE routine_name='fact';+------+------+----------------------------------------------+----+| s | n | def | l |+------+------+----------------------------------------------+----+| test | fact | let result = 1;while (n > 1) {result *= n;n--;}return result;| JS |+------+------+---------------------------------------------+----+1 row in set (0.01 sec)mysql> DROP FUNCTION fact; |
`CREATE FUNCTION` 또는 `PROCEDURE` 문에서 선언하는 매개변수는 JS 코드에서 접근할 수 있으며 OUT 및 INOUT 매개변수도 지원됩니다.
JS `return` 문으로 반환된 값은 stored function 의 반환 값이 됩니다.
대부분의 SQL 데이터 타입이 매개변수로 지원합니다.
UTF-8 이외의 문자 집합을 사용하는 문자 데이터는 JS 코드 내에서 처리될 수 있도록 자동으로 UTF-8 로 변환됩니다.
이 변환은 반환 값이나 OUT/INOUT 매개변수가 필요한 경우 역으로 수행됩니다.
BLOB, BINARY 및 VARBINARY 는 DataView 객체로 매핑됩니다.
`JSON` SQL 데이터 타입의 매개변수는 JS 객체로 변환됩니다.
반환 값, OUT 및 INOUT 매개변수는 그 반대 방향으로 변환됩니다.
이 기능은 매우 잘 작동하기 때문에 별도의 예시가 필요할 정도입니다.
mysql> CREATE FUNCTION norm_keys(d JSON) RETURNS JSON LANGUAGE JS AS $$ $> const processData = (j) => { $> // Handle different possible structures $> if (Array.isArray(j)) { $> return j.map(i => processData(i)); $> } else if (typeof j === 'object') { $> const r = {}; $> for (const key in j) { $> // Transform keys to lowercase for consistency $> const normKey = key.toLowerCase(); $> r[normKey] = processData(j[key]); $> } $> return r; $> } $> return j; $> }; $> $> return processData(d); $> $$;Query OK, 0 rows affected (0.01 sec)mysql> SELECT norm_keys('{ '> "UserName": "John", '> "Age": 30, '> "Contacts": [ { "type": "email", "value": "john@example.com" }, '> { "type": "phone", "value": "123-456-7890" } ], '> "metadata": { "lastLogin": "2023-01-01", "isActive": true } '> }') AS n;+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| n |+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| {"age": 30, "contacts": [{"type": "email", "value": "john@example.com"}, {"type": "phone", "value": "123-456-7890"}], "metadata": {"isactive": true, "lastlogin": "2023-01-01"}, "username": "John"} |+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.00 sec) |
각 connection 및 각 active 사용자마다 자신만의 JS 컨텍스트를 가지며, 이는 다른 connection 과 공유되지 않고 (사용성 측면), 같은 connection 내의 다른 사용자와도 공유되지 않습니다 (보안 측면).
Percona 는 V8 엔진이 제공하는 동일 수준의 JS 언어 지원을 제공합니다.
즉, 표준 연산자, 데이터 타입, 객체(예: Math) 및 ECMA 표준의 함수들은 지원되지만, 브라우저에서 제공되는 DOM 모델과 같은 기능은 지원되지 않음을 의미합니다.
또한, 데이터베이스 보안을 우회할 수 있는 네트워크 또는 파일 I/O 도 지원하지 않습니다.
현재 connection 및 사용자에 대해 발생한 마지막 JS 오류에 대한 정보를 얻으려면 우리 컴포넌트가 제공하는 JS_GET_LAST_ERROR() 및 JS_GET_LAST_ERROR_INFO() UDF를 사용할 수 있습니다.
예를 들면 다음과 같습니다:
mysql> CREATE PROCEDURE p_error() LANGUAGE JS AS $$$> function l0() {$> return "Ooops!"();$> }$> function l1() {$> return l0();$> }$> (() => { return l1(); })();$> $$;Query OK, 0 rows affected (0.01 sec)mysql> CALL p_error();ERROR 6000 (HY000): TypeError: "Ooops!" is not a functionmysql> SELECT js_get_last_error_info();+----------------------------------------------------------+| js_get_last_error_info() |+----------------------------------------------------------+| Error: TypeError: "Ooops!" is not a function At: SQL PROCEDURE test.p_error:3:19 Line: return "Ooops!"();^
|
또한, 표준 JS 콘솔 로그 API(https://console.spec.whatwg.org/)를 사용하여 JS stored programs 을 디버깅할 수 있습니다. 예를 들면 다음과 같습니다:
mysql> CREATE PROCEDURE p1() LANGUAGE JS AS $$ console.log("Test!") $$;Query OK, 0 rows affected (0.01 sec)mysql> CALL p1();Query OK, 0 rows affected (0.01 sec)mysql> SELECT JS_GET_CONSOLE_LOG();+----------------------+| JS_GET_CONSOLE_LOG() |+----------------------+| Test! |+----------------------+1 row in set (0.00 sec)mysql> SELECT JS_GET_CONSOLE_LOG_JSON();+----------------------------------------------------------+| JS_GET_CONSOLE_LOG_JSON() |+----------------------------------------------------------+| [ {"timestamp": "2025-05-27 19:13:25.654906","level": "Info","message": "Test!"}] |+----------------------------------------------------------+1 row in set (0.00 sec) |
위 예시에서 볼 수 있듯이, 현재 connection 및 사용자에 대한 console log 출력을 확인하기 위해 JS_GET_CONSOLE_LOG() 와 JS_GET_CONSOLE_LOG_JSON() UDF 를 사용할 수 있습니다.
기본 형식과 확장 형식 모두 지원됩니다.
또한, console log 를 초기화하는 JS_CLEAR_CONSOLE_LOG() UDF 도 존재합니다.
JS 로 작성된 stored programs 이 너무 오래 실행되거나 무한 루프에 빠진 경우, KILL QUERY SQL 문을 사용하여 실행을 쉽게 중단할 수 있습니다.
MAX_EXECUTION_TIME hint 및 변수도 작동합니다.
Limitations and what’s coming next
우리 단기 계획에 아직 작동하지 않는 몇 가지 사항이 있습니다:
- JS 안에서 Stored Programs 메모리 사용량 추적 및 제한 (GitHub 버전에서는 작동하지만 아직 제대로 테스트되지 않았습니다).
- JS 안의 Stored Programs 에서 SQL 실행
Help us shape the future: We want your feedback
저희는 특히 JavaScript 로 개발하시는 사용자분들께 이 새로운 실험적 기능을 사용해 보시고, 발견하신 문제나 개선할 점에 대한 의견을 공유해 주시길 부탁드립니다!
의견은 Percona 커뮤니티 포럼이나 저희 JIRA를 통해 전달하실 수 있습니다.
이 초기 단계에서 여러분의 피드백을 반영하는 것이 훨씬 수월하니 많은 참여 부탁드립니다!
자유롭게 댓글을 달아주세요! 언제나 환영합니다.
기타 문의: info@neoclova.co.kr
네오클로바 기술블로그 홈 바로가기: https://neoclova.net
네오클로바 홈페이지: http://neoclova.co.kr
