This is an automated email from the ASF dual-hosted git repository.
pingsutw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git
The following commit(s) were added to refs/heads/master by this push:
new c132986 SUBMARINE-1002. Submarine model management database and
database sdk
c132986 is described below
commit c132986aec660c8bed2f7b8bd5af9bba0a22be7d
Author: KUAN-HSUN-LI <[email protected]>
AuthorDate: Sun Sep 12 18:39:29 2021 +0800
SUBMARINE-1002. Submarine model management database and database sdk
### What is this PR for?
1. Create submarine model management database including
`registered_models`, `registered_model_tags`, `model_versions` and
`model_version_tags` tables.
2. Move the `params` and `metrics` tables from `submarine.sql` to
`submarine-model.sql`
### What type of PR is it?
[Feature]
### Todos
### What is the Jira issue?
https://issues.apache.org/jira/browse/SUBMARINE-1002
### How should this be tested?
run the sdk unit tests `pytest --cov=submarine -vs -m "not e2e"`
TestModelVersion and TestRegisteredModel are two relevant unit tests
### Screenshots (if appropriate)
### Questions:
* Do the license files need updating? No
* Are there breaking changes for older versions? No
* Does this need new documentation? No
Author: KUAN-HSUN-LI <[email protected]>
Signed-off-by: Kevin <[email protected]>
Closes #731 from KUAN-HSUN-LI/SUBMARINE-1002 and squashes the following
commits:
ec551448 [KUAN-HSUN-LI] add model version on delete cascade
245126d0 [KUAN-HSUN-LI] fix conflict
09615f7d [KUAN-HSUN-LI] SUBMARINE-1002. Update comment
54d4242d [KUAN-HSUN-LI] fix conflict
82f80762 [KUAN-HSUN-LI] change datetime precision
aef97310 [KUAN-HSUN-LI] fix datetime default value
4a6998a3 [KUAN-HSUN-LI] replace database table with sigular name
0c504607 [KUAN-HSUN-LI] change bigint to datetime
9fd00a30 [KUAN-HSUN-LI] change table name from model_version_tags to
model_tags
8aa9ec45 [KUAN-HSUN-LI] SUBMARINE-1002. foreign key support
5b522e85 [KUAN-HSUN-LI] SUBMARINE-1002. foreign key for metrics and params
65608f71 [KUAN-HSUN-LI] fix lint
e58d7ddc [KUAN-HSUN-LI] SUBMARINE-1002. submarine-model-database sdk test
86254d96 [KUAN-HSUN-LI] handle tag
160dc049 [KUAN-HSUN-LI] change directory
4d536b80 [KUAN-HSUN-LI] fix
e0950bed [KUAN-HSUN-LI] SUBMARINE-1002. submarine model database sdk
337f038c [KUAN-HSUN-LI] fix database docker image
8f127208 [KUAN-HSUN-LI] SUBMARINE-1002. Create submarine model management
table
---
dev-support/database/README.md | 4 +-
dev-support/database/init-database.py | 2 +
dev-support/database/init-database.sh | 4 +-
dev-support/database/submarine-data.sql | 21 --
dev-support/database/submarine-model.sql | 79 +++++
dev-support/database/submarine.sql | 28 --
dev-support/docker-images/database/startup.sh | 6 +-
.../pysubmarine/submarine/entities/Metric.py | 2 +-
.../pysubmarine/submarine/entities/__init__.py | 2 +
.../pysubmarine/submarine/entities/experiment.py | 60 ++++
.../entities/{ => model_registry}/__init__.py | 12 +-
.../{__init__.py => model_registry/model_tag.py} | 20 +-
.../entities/model_registry/model_version.py | 106 ++++++
.../model_version_stages.py} | 11 +-
.../entities/model_registry/registered_model.py | 57 +++
.../registered_model_tag.py} | 20 +-
.../pysubmarine/submarine/store/database/models.py | 390 +++++++++++++++++++--
.../pysubmarine/submarine/tracking/fluent.py | 6 +-
.../pysubmarine/submarine/utils/validation.py | 7 +-
.../entities/model_registry/test_model_version.py | 125 +++++++
.../model_registry/test_registered_model.py | 74 ++++
.../pysubmarine/tests/entities/test_metrics.py | 4 +-
.../tests/store/test_sqlalchemy_store.py | 20 +-
.../pysubmarine/tests/tracking/test_tracking.py | 17 +-
.../server/workbench/database/entity/Metric.java | 10 +-
.../server/workbench/rest/MetricRestApi.java | 9 +-
.../submarine/database/mappers/MetricMapper.xml | 20 +-
.../submarine/database/mappers/ParamMapper.xml | 12 +-
.../database/service/MetricServiceTest.java | 38 +-
.../database/service/ParamServiceTest.java | 23 +-
30 files changed, 1040 insertions(+), 149 deletions(-)
diff --git a/dev-support/database/README.md b/dev-support/database/README.md
index f55f6a1..39047fa 100644
--- a/dev-support/database/README.md
+++ b/dev-support/database/README.md
@@ -92,10 +92,11 @@ Run [mysql docker container](https://hub.docker.com/_/mysql)
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
```
-Copy the files, submarine.sql, submarine-data.sql and metastore.sql to the
mysql docker.
+Copy the files, submarine.sql, submarine-model.sql, submarine-data.sql and
metastore.sql to the mysql docker.
```
docker cp ${SUBMARINE_HOME}/dev-support/database/submarine.sql ${DOCKER_ID}:/
+docker cp ${SUBMARINE_HOME}/dev-support/database/submarine-model.sql
${DOCKER_ID}:/
docker cp ${SUBMARINE_HOME}/dev-support/database/submarine-data.sql
${DOCKER_ID}:/
docker cp ${SUBMARINE_HOME}/dev-support/database/metastore.sql ${DOCKER_ID}:/
```
@@ -110,6 +111,7 @@ mysql> GRANT ALL PRIVILEGES ON * . * TO 'submarine'@'%';
mysql> CREATE DATABASE IF NOT EXISTS submarine CHARACTER SET utf8 COLLATE
utf8_general_ci;
mysql> use submarine;
mysql> source /submarine.sql;
+mysql> source /submarine-model.sql;
mysql> source /submarine-data.sql;
mysql> CREATE USER IF NOT EXISTS 'metastore'@'%' IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON * . * TO 'metastore'@'%';
diff --git a/dev-support/database/init-database.py
b/dev-support/database/init-database.py
index aa1ac69..6df1131 100644
--- a/dev-support/database/init-database.py
+++ b/dev-support/database/init-database.py
@@ -52,6 +52,7 @@ commit("CREATE USER IF NOT EXISTS 'submarine_test'@'%'
IDENTIFIED BY 'password_t
commit("GRANT ALL PRIVILEGES ON *.* TO 'submarine_test'@'%';")
commit("use submarine_test;")
commit_from_file("./dev-support/database/submarine.sql")
+commit_from_file("./dev-support/database/submarine-model.sql")
commit("show tables;")
@@ -68,6 +69,7 @@ commit("CREATE USER IF NOT EXISTS 'submarine'@'%' IDENTIFIED
BY 'password';")
commit("GRANT ALL PRIVILEGES ON *.* TO 'submarine'@'%';")
commit("use submarine;")
commit_from_file("./dev-support/database/submarine.sql")
+commit_from_file("./dev-support/database/submarine-model.sql")
commit_from_file("./dev-support/database/submarine-data.sql")
commit("show tables;")
diff --git a/dev-support/database/init-database.sh
b/dev-support/database/init-database.sh
index 0c3b37b..4288d36 100755
--- a/dev-support/database/init-database.sh
+++ b/dev-support/database/init-database.sh
@@ -23,6 +23,7 @@ mysql -e "CREATE DATABASE IF NOT EXISTS submarine_test;"
mysql -e "CREATE USER IF NOT EXISTS 'submarine_test'@'%' IDENTIFIED BY
'password_test';"
mysql -e "GRANT ALL PRIVILEGES ON *.* TO 'submarine_test'@'%';"
mysql -e "use submarine_test; source ./submarine.sql; show tables;"
+mysql -e "use submarine_test; source ./submarine-model.sql; show tables;"
mysql -e "CREATE DATABASE IF NOT EXISTS metastore_test;"
mysql -e "CREATE USER IF NOT EXISTS 'metastore_test'@'%' IDENTIFIED BY
'password_test';"
@@ -32,7 +33,8 @@ mysql -e "use metastore_test; source ./metastore.sql; show
tables;"
mysql -e "CREATE DATABASE IF NOT EXISTS submarine;"
mysql -e "CREATE USER IF NOT EXISTS 'submarine'@'%' IDENTIFIED BY 'password';"
mysql -e "GRANT ALL PRIVILEGES ON *.* TO 'submarine'@'%';"
-mysql -e "use submarine; source ./submarine.sql; source ./submarine-data.sql;
show tables;"
+mysql -e "use submarine; source ./submarine.sql; source ./submarine-model.sql;"
+mysql -e "use submarine; source ./submarine-data.sql; show tables;"
mysql -e "CREATE DATABASE IF NOT EXISTS metastore;"
mysql -e "CREATE USER IF NOT EXISTS 'metastore'@'%' IDENTIFIED BY 'password';"
diff --git a/dev-support/database/submarine-data.sql
b/dev-support/database/submarine-data.sql
index 0084fca..a7a3743 100644
--- a/dev-support/database/submarine-data.sql
+++ b/dev-support/database/submarine-data.sql
@@ -61,27 +61,6 @@ INSERT INTO `sys_user` VALUES
('e9ca23d68d884d4ebb19d07889727dae', 'admin', 'adm
INSERT INTO `team` VALUES ('e9ca23d68d884d4ebb19d07889721234', 'admin',
'Submarine', 'admin', '2020-05-06 14:00:05', 'Jack', '2020-05-06 14:00:14');
-- ----------------------------
--- Records of metrics
--- ----------------------------
-INSERT INTO `metrics` (`id`, `key`, `value`, `worker_index`, `timestamp`,
`step`, `is_nan`) VALUES
-('application_123651651', 'score', 0.666667, 'worker-1', 1569139525097, 0, 0),
-('application_123651651', 'score', 0.666670, 'worker-1', 1569149139731, 1, 0),
-('experiment_1595332719154_0001', 'score', 0.666667, 'worker-1',
1569169376482, 0, 0),
-('experiment_1595332719154_0001', 'score', 0.666671, 'worker-1',
1569236290721, 0, 0),
-('experiment_1595332719154_0001', 'score', 0.666680, 'worker-1',
1569236466722, 0, 0);
-
--- ----------------------------
--- Records of params
--- ----------------------------
-INSERT INTO `params` (`id`, `key`, `value`, `worker_index`) VALUES
-('application_123651651', 'max_iter', '100', 'worker-1'),
-('application_123456898', 'n_jobs', '5', 'worker-1'),
-('application_123456789', 'alpha', '20', 'worker-1'),
-('experiment_1595332719154_0001', 'max_iter', '100', 'worker-1'),
-('experiment_1595332719154_0002', 'n_jobs', '5', 'worker-1'),
-('experiment_1595332719154_0003', 'alpha', '20', 'worker-1');
-
--- ----------------------------
-- Records of environment
-- ----------------------------
INSERT INTO `environment` VALUES
diff --git a/dev-support/database/submarine-model.sql
b/dev-support/database/submarine-model.sql
new file mode 100644
index 0000000..da124e9
--- /dev/null
+++ b/dev-support/database/submarine-model.sql
@@ -0,0 +1,79 @@
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License. You may obtain a copy of the License at
+-- http://www.apache.org/licenses/LICENSE-2.0
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+DROP TABLE IF EXISTS `registered_model`;
+CREATE TABLE `registered_model` (
+ `name` VARCHAR(256) NOT NULL,
+ `creation_time` DATETIME(3) COMMENT 'Millisecond precision',
+ `last_updated_time` DATETIME(3) COMMENT 'Millisecond precision',
+ `description` VARCHAR(5000),
+ CONSTRAINT `registered_model_pk` PRIMARY KEY (`name`),
+ UNIQUE (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `registered_model_tag`;
+CREATE TABLE `registered_model_tag` (
+ `name` VARCHAR(256) NOT NULL,
+ `tag` VARCHAR(256) NOT NULL,
+ CONSTRAINT `registered_model_tag_pk` PRIMARY KEY (`name`, `tag`),
+ FOREIGN KEY(`name`) REFERENCES `registered_model` (`name`) ON UPDATE
CASCADE ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `model_version`;
+CREATE TABLE `model_version` (
+ `name` VARCHAR(256) NOT NULL,
+ `version` INTEGER NOT NULL,
+ `user_id` VARCHAR(64) NOT NULL COMMENT 'Id of the created user',
+ `experiment_id` VARCHAR(64) NOT NULL,
+ `current_stage` VARCHAR(20) COMMENT 'Model stage ex: None,
production...',
+ `creation_time` DATETIME(3) COMMENT 'Millisecond precision',
+ `last_updated_time` DATETIME(3) COMMENT 'Millisecond precision',
+ `source` VARCHAR(512) COMMENT 'Model saved link',
+ `dataset` VARCHAR(256) COMMENT 'Which dataset is used',
+ `description` VARCHAR(5000),
+ CONSTRAINT `model_version_pk` PRIMARY KEY (`name`, `version`),
+ FOREIGN KEY(`name`) REFERENCES `registered_model` (`name`) ON UPDATE
CASCADE ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `model_tag`;
+CREATE TABLE `model_tag` (
+ `name` VARCHAR(256) NOT NULL,
+ `version` INTEGER NOT NULL,
+ `tag` VARCHAR(256) NOT NULL,
+ CONSTRAINT `model_tag_pk` PRIMARY KEY (`name`, `version`, `tag`),
+ FOREIGN KEY(`name`, `version`) REFERENCES `model_version` (`name`,
`version`) ON UPDATE CASCADE ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `metric`;
+CREATE TABLE `metric` (
+ `id` VARCHAR(64) NOT NULL COMMENT 'Id of the Experiment',
+ `key` VARCHAR(190) NOT NULL COMMENT 'Metric key: `String` (limit 190
characters). Part of *Primary Key* for ``metric`` table.',
+ `value` FLOAT NOT NULL COMMENT 'Metric value: `Float`. Defined as
*Non-null* in schema.',
+ `worker_index` VARCHAR(32) NOT NULL COMMENT 'Metric worker_index:
`String` (limit 32 characters). Part of *Primary Key* for\r\n ``metrics``
table.',
+ `timestamp` DATETIME(3) NOT NULL COMMENT 'Timestamp recorded for this
metric entry: `DATETIME` (millisecond precision).
+
Part of *Primary Key* for ``metrics`` table.',
+ `step` INTEGER NOT NULL COMMENT 'Step recorded for this metric entry:
`INTEGER`.',
+ `is_nan` BOOLEAN NOT NULL COMMENT 'True if the value is in fact NaN.',
+ CONSTRAINT `metric_pk` PRIMARY KEY (`id`, `key`, `timestamp`,
`worker_index`),
+ FOREIGN KEY(`id`) REFERENCES `experiment` (`id`) ON UPDATE CASCADE ON
DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `param`;
+CREATE TABLE `param` (
+ `id` VARCHAR(64) NOT NULL COMMENT 'Id of the Experiment',
+ `key` VARCHAR(190) NOT NULL COMMENT '`String` (limit 190 characters).
Part of *Primary Key* for ``param`` table.',
+ `value` VARCHAR(32) NOT NULL COMMENT '`String` (limit 190 characters).
Defined as *Non-null* in schema.',
+ `worker_index` VARCHAR(32) NOT NULL COMMENT '`String` (limit 32
characters). Part of *Primary Key* for\r\n ``metric`` table.',
+ CONSTRAINT `param_pk` PRIMARY KEY (`id`, `key`, `worker_index`),
+ FOREIGN KEY(`id`) REFERENCES `experiment` (`id`) ON UPDATE CASCADE ON
DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/dev-support/database/submarine.sql
b/dev-support/database/submarine.sql
index 04f8b51..a2a89e6 100644
--- a/dev-support/database/submarine.sql
+++ b/dev-support/database/submarine.sql
@@ -265,34 +265,6 @@ CREATE TABLE `notebook` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
--- Table structure for metric
--- ----------------------------
-DROP TABLE IF EXISTS `metrics`;
-CREATE TABLE `metrics` (
- `id` varchar(64) NOT NULL COMMENT 'Id of the Experiment',
- `key` varchar(190) NOT NULL COMMENT 'Metric key: `String` (limit 190
characters). Part of *Primary Key* for ``metrics`` table.',
- `value` float NOT NULL COMMENT 'Metric value: `Float`. Defined as *Non-null*
in schema.',
- `worker_index` varchar(32) NOT NULL COMMENT 'Metric worker_index: `String`
(limit 32 characters). Part of *Primary Key* for\r\n ``metrics`` table.',
- `timestamp` bigint(20) NOT NULL COMMENT 'Timestamp recorded for this metric
entry: `BigInteger`. Part of *Primary Key* for ``metrics`` table.',
- `step` bigint(11) NOT NULL COMMENT 'Step recorded for this metric entry:
`BigInteger`.',
- `is_nan` BOOLEAN NOT NULL COMMENT 'True if the value is in fact NaN.',
- PRIMARY KEY (`id`, `key`, `timestamp`, `worker_index`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
--- ----------------------------
--- Table structure for param
--- ----------------------------
-DROP TABLE IF EXISTS `params`;
-CREATE TABLE `params` (
- `id` varchar(64) NOT NULL COMMENT 'Id of the Experiment',
- `key` varchar(190) NOT NULL COMMENT '`String` (limit 190 characters). Part
of *Primary Key* for ``params`` table.',
- `value` varchar(32) NOT NULL COMMENT '`String` (limit 190 characters).
Defined as *Non-null* in schema.',
- `worker_index` varchar(32) NOT NULL COMMENT '`String` (limit 32 characters).
Part of *Primary Key* for\r\n ``metrics`` table.',
- PRIMARY KEY (`id`, `key`, `worker_index`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-
--- ----------------------------
-- Table structure for experiment_templates
-- ----------------------------
DROP TABLE IF EXISTS `experiment_template`;
diff --git a/dev-support/docker-images/database/startup.sh
b/dev-support/docker-images/database/startup.sh
index cce03a8..c52fc93 100755
--- a/dev-support/docker-images/database/startup.sh
+++ b/dev-support/docker-images/database/startup.sh
@@ -18,11 +18,13 @@ mysql -uroot -p$MYSQL_ROOT_PASSWORD <<EOF
CREATE DATABASE submarine_test;
CREATE USER 'submarine_test'@'%' IDENTIFIED BY 'password_test';
GRANT ALL PRIVILEGES ON *.* TO 'submarine_test'@'%';
-use submarine_test; source /tmp/database/submarine.sql; source
/tmp/database/submarine-data.sql;
+use submarine_test; source /tmp/database/submarine.sql; source
/tmp/database/submarine-model.sql;
+source /tmp/database/submarine-data.sql;
CREATE DATABASE submarine;
CREATE USER 'submarine'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'submarine'@'%';
-use submarine; source /tmp/database/submarine.sql; source
/tmp/database/submarine-data.sql;
+use submarine; source /tmp/database/submarine.sql; source
/tmp/database/submarine-model.sql;
+source /tmp/database/submarine-data.sql;
CREATE DATABASE metastore_test;
CREATE USER 'metastore_test'@'%' IDENTIFIED BY 'password_test';
GRANT ALL PRIVILEGES ON * . * TO 'metastore_test'@'%';
diff --git a/submarine-sdk/pysubmarine/submarine/entities/Metric.py
b/submarine-sdk/pysubmarine/submarine/entities/Metric.py
index ea69954..df2f00f 100644
--- a/submarine-sdk/pysubmarine/submarine/entities/Metric.py
+++ b/submarine-sdk/pysubmarine/submarine/entities/Metric.py
@@ -45,7 +45,7 @@ class Metric(_SubmarineObject):
@property
def timestamp(self):
- """Metric timestamp as an integer (milliseconds since the Unix
epoch)."""
+ """Metric timestamp as aa datetime object."""
return self._timestamp
@property
diff --git a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
b/submarine-sdk/pysubmarine/submarine/entities/__init__.py
index e2d8479..323e7e4 100644
--- a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
+++ b/submarine-sdk/pysubmarine/submarine/entities/__init__.py
@@ -13,10 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from submarine.entities.experiment import Experiment
from submarine.entities.Metric import Metric
from submarine.entities.Param import Param
__all__ = [
+ "Experiment",
"Metric",
"Param",
]
diff --git a/submarine-sdk/pysubmarine/submarine/entities/experiment.py
b/submarine-sdk/pysubmarine/submarine/entities/experiment.py
new file mode 100644
index 0000000..ffd89ec
--- /dev/null
+++ b/submarine-sdk/pysubmarine/submarine/entities/experiment.py
@@ -0,0 +1,60 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from submarine.entities._submarine_object import _SubmarineObject
+
+
+class Experiment(_SubmarineObject):
+ """
+ Experiment object.
+ """
+
+ def __init__(self, id, experiment_spec, create_by, create_time, update_by,
update_time):
+ self._id = id
+ self._experiment_spec = experiment_spec
+ self._create_by = create_by
+ self._create_time = create_time
+ self._update_by = update_by
+ self._update_time = update_time
+
+ @property
+ def id(self):
+ """String ID of the experiment."""
+ return self._id
+
+ @property
+ def experiment_spec(self):
+ """String of the experiment spec."""
+ return self._experiment_spec
+
+ @property
+ def create_by(self):
+ """String name of created user id."""
+ return self.create_by
+
+ @property
+ def create_time(self):
+ """Datetime of create time."""
+ return self._create_time
+
+ @property
+ def update_by(self):
+ """String name of updated user id"."""
+ return self._update_by
+
+ @property
+ def update_time(self):
+ """Datetime of update time."""
+ return self._update_time
diff --git a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/__init__.py
similarity index 67%
copy from submarine-sdk/pysubmarine/submarine/entities/__init__.py
copy to submarine-sdk/pysubmarine/submarine/entities/model_registry/__init__.py
index e2d8479..7f537e8 100644
--- a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
+++ b/submarine-sdk/pysubmarine/submarine/entities/model_registry/__init__.py
@@ -13,10 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from submarine.entities.Metric import Metric
-from submarine.entities.Param import Param
+from submarine.entities.model_registry.model_tag import ModelTag
+from submarine.entities.model_registry.model_version import ModelVersion
+from submarine.entities.model_registry.registered_model import RegisteredModel
+from submarine.entities.model_registry.registered_model_tag import
RegisteredModelTag
__all__ = [
- "Metric",
- "Param",
+ "ModelVersion",
+ "ModelTag",
+ "RegisteredModel",
+ "RegisteredModelTag",
]
diff --git a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/model_tag.py
similarity index 71%
copy from submarine-sdk/pysubmarine/submarine/entities/__init__.py
copy to submarine-sdk/pysubmarine/submarine/entities/model_registry/model_tag.py
index e2d8479..5cbcae1 100644
--- a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
+++ b/submarine-sdk/pysubmarine/submarine/entities/model_registry/model_tag.py
@@ -13,10 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from submarine.entities.Metric import Metric
-from submarine.entities.Param import Param
+from submarine.entities._submarine_object import _SubmarineObject
-__all__ = [
- "Metric",
- "Param",
-]
+
+class ModelTag(_SubmarineObject):
+ """
+ Tag object associated with a model version.
+ """
+
+ def __init__(self, tag):
+ self._tag = tag
+
+ @property
+ def tag(self):
+ """String tag"""
+ return self._tag
diff --git
a/submarine-sdk/pysubmarine/submarine/entities/model_registry/model_version.py
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/model_version.py
new file mode 100644
index 0000000..88c9ff4
--- /dev/null
+++
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/model_version.py
@@ -0,0 +1,106 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from submarine.entities._submarine_object import _SubmarineObject
+
+
+class ModelVersion(_SubmarineObject):
+ """
+ Model Version object.
+ """
+
+ def __init__(
+ self,
+ name,
+ version,
+ user_id,
+ experiment_id,
+ current_stage,
+ creation_time,
+ last_updated_time,
+ source,
+ dataset=None,
+ description=None,
+ tags=None,
+ ):
+ self._name = name
+ self._version = version
+ self._user_id = user_id
+ self._experiment_id = experiment_id
+ self._current_stage = current_stage
+ self._creation_time = creation_time
+ self._last_updated_time = last_updated_time
+ self._source = source
+ self._dataset = dataset
+ self._description = description
+ self._tags = [tag.tag for tag in (tags or [])]
+
+ @property
+ def name(self):
+ """String. Unique name within Model Registry."""
+ return self._name
+
+ @property
+ def version(self):
+ """String. version"""
+ return self._version
+
+ @property
+ def user_id(self):
+ """String. User ID that created this model version."""
+ return self._user_id
+
+ @property
+ def experiment_id(self):
+ """String. Experiment ID that created this model version."""
+ return self._experiment_id
+
+ @property
+ def creation_time(self):
+ """Datetime object. Model version creation timestamp."""
+ return self._creation_time
+
+ @property
+ def last_updated_time(self):
+ """Datetime object. Timestamp of last update for this model version."""
+ return self._last_updated_time
+
+ @property
+ def source(self):
+ """String. Source path for the model."""
+ return self._source
+
+ @property
+ def current_stage(self):
+ """String. Current stage of this model version."""
+ return self._current_stage
+
+ @property
+ def dataset(self):
+ """String. Dataset used by this model version"""
+ return self._dataset
+
+ @property
+ def description(self):
+ """String. Description"""
+ return self._description
+
+ @property
+ def tags(self):
+ """List of strings"""
+ return self._tags
+
+ def _add_tag(self, tag):
+ self._tags.append(tag)
diff --git a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/model_version_stages.py
similarity index 81%
copy from submarine-sdk/pysubmarine/submarine/entities/__init__.py
copy to
submarine-sdk/pysubmarine/submarine/entities/model_registry/model_version_stages.py
index e2d8479..a8fae8e 100644
--- a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
+++
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/model_version_stages.py
@@ -13,10 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from submarine.entities.Metric import Metric
-from submarine.entities.Param import Param
+STAGE_NONE = "None"
+STAGE_STAGING = "Staging"
+STAGE_PRODUCTION = "Production"
+STAGE_ARCHIVED = "Archived"
-__all__ = [
- "Metric",
- "Param",
-]
+ALL_STAGES = [STAGE_NONE, STAGE_STAGING, STAGE_PRODUCTION, STAGE_ARCHIVED]
diff --git
a/submarine-sdk/pysubmarine/submarine/entities/model_registry/registered_model.py
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/registered_model.py
new file mode 100644
index 0000000..7ff5e69
--- /dev/null
+++
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/registered_model.py
@@ -0,0 +1,57 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from submarine.entities._submarine_object import _SubmarineObject
+
+
+class RegisteredModel(_SubmarineObject):
+ """
+ Registered Model object.
+ """
+
+ def __init__(self, name, creation_time, last_updated_time,
description=None, tags=None):
+ self._name = name
+ self._creation_time = creation_time
+ self._last_updated_time = last_updated_time
+ self._description = description
+ self._tags = [tag.tag for tag in (tags or [])]
+
+ @property
+ def name(self):
+ """String. Registered model name."""
+ return self._name
+
+ @property
+ def creation_time(self):
+ """Datetime object. Model version creation timestamp."""
+ return self._creation_time
+
+ @property
+ def last_updated_time(self):
+ """Datetime object. Timestamp of last update for this model version."""
+ return self._last_updated_time
+
+ @property
+ def description(self):
+ """String. Description"""
+ return self._description
+
+ @property
+ def tags(self):
+ """List of strings"""
+ return self._tags
+
+ def _add_tag(self, tag):
+ self._tags.append(tag)
diff --git a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/registered_model_tag.py
similarity index 71%
copy from submarine-sdk/pysubmarine/submarine/entities/__init__.py
copy to
submarine-sdk/pysubmarine/submarine/entities/model_registry/registered_model_tag.py
index e2d8479..22de3c9 100644
--- a/submarine-sdk/pysubmarine/submarine/entities/__init__.py
+++
b/submarine-sdk/pysubmarine/submarine/entities/model_registry/registered_model_tag.py
@@ -13,10 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from submarine.entities.Metric import Metric
-from submarine.entities.Param import Param
+from submarine.entities._submarine_object import _SubmarineObject
-__all__ = [
- "Metric",
- "Param",
-]
+
+class RegisteredModelTag(_SubmarineObject):
+ """
+ Tag object associated with a registered model.
+ """
+
+ def __init__(self, tag):
+ self._tag = tag
+
+ @property
+ def tag(self):
+ """String tag."""
+ return self._tag
diff --git a/submarine-sdk/pysubmarine/submarine/store/database/models.py
b/submarine-sdk/pysubmarine/submarine/store/database/models.py
index c4593d7..a1caffe 100644
--- a/submarine-sdk/pysubmarine/submarine/store/database/models.py
+++ b/submarine-sdk/pysubmarine/submarine/store/database/models.py
@@ -13,39 +13,380 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import time
+from datetime import datetime
from typing import Any
import sqlalchemy as sa
-from sqlalchemy import BigInteger, Boolean, Column, PrimaryKeyConstraint,
String
+from sqlalchemy import (
+ BigInteger,
+ Boolean,
+ Column,
+ ForeignKey,
+ ForeignKeyConstraint,
+ Integer,
+ PrimaryKeyConstraint,
+ String,
+ Text,
+)
+from sqlalchemy.dialects.mysql import DATETIME
from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import backref, relationship
-from submarine.entities import Metric, Param
+from submarine.entities import Experiment, Metric, Param
+from submarine.entities.model_registry import (
+ ModelTag,
+ ModelVersion,
+ RegisteredModel,
+ RegisteredModelTag,
+)
+from submarine.entities.model_registry.model_version_stages import STAGE_NONE
# Base class in sqlalchemy is a dynamic type
Base: Any = declarative_base()
+#
+---------------------+-------------------------+-------------------------+-------------+
+# | name | creation_time | last_updated_time |
description |
+#
+---------------------+-------------------------+-------------------------+-------------+
+# | image_classfication | 2021-08-31 11:11:11.111 | 2021-09-02 11:11:11.111 |
... |
+# | speech_recoginition | 2021-08-31 16:16:16.166 | 2021-08-31 20:20:20.200 |
... |
+#
+---------------------+-------------------------+-------------------------+-------------+
-#
+--------------------+-------+-------------------+--------------+---------------+------+--------+
-# | id | key | value | worker_index | timestamp
| step | is_nan |
-#
+--------------------+-------+-------------------+--------------+---------------+------+--------+
-# | application_123456 | score | 0.666666666666667 | worker-1 |
1595414873838 | 0 | 0 |
-# | application_123456 | score | 0.666666666666667 | worker-1 |
1595472286360 | 0 | 0 |
-# | application_123456 | score | 0.666666666666667 | worker-1 |
1595414632967 | 0 | 0 |
-# | application_123456 | score | 0.666666666666667 | worker-1 |
1595415075067 | 0 | 0 |
-#
+--------------------+-------+-------------------+--------------+---------------+------+--------+
+class SqlRegisteredModel(Base):
+ __tablename__ = "registered_model"
+
+ name = Column(String(256), unique=True, nullable=False)
+ """
+ Name for registered models: Part of *Primary Key* for ``registered_model``
table.
+ """
+
+ creation_time = Column(DATETIME(fsp=3), default=datetime.now())
+ """
+ Creation time of registered models: default current time in milliseconds
+ """
+
+ last_updated_time = Column(DATETIME(fsp=3), nullable=True, default=None)
+ """
+ Last updated time of registered model
+ """
+
+ description = Column(String(5000), nullable=True, default="")
+ """
+ Description for registered model
+ """
+
+ __table_args__ = (PrimaryKeyConstraint("name",
name="registered_model_pk"),)
+
+ def __repr__(self):
+ return "<SqlRegisteredModel ({}, {}, {}, {})>".format(
+ self.name, self.creation_time, self.last_updated_time,
self.description
+ )
+
+ def to_submarine_entity(self):
+ """
+ Convert DB model to corresponding Submarine entity.
+ :return: :py:class:`submarine.entities.RegisteredModel`.
+ """
+ return RegisteredModel(
+ name=self.name,
+ creation_time=self.creation_time,
+ last_updated_time=self.last_updated_time,
+ description=self.description,
+ tags=[tag.to_submarine_entity for tag in
self.registered_model_tag],
+ )
+
+
+# +---------------------+-------+
+# | name | tag |
+# +---------------------+-------+
+# | image_classfication | image |
+# | image_classfication | major |
+# | speech_recoginition | audio |
+# +---------------------+-------+
+
+
+class SqlRegisteredModelTag(Base):
+ __tablename__ = "registered_model_tag"
+
+ name = Column(
+ String(256), ForeignKey("registered_model.name", onupdate="cascade",
ondelete="cascade")
+ )
+ """
+ Name for registered models: Part of *Primary Key* for
``registered_model_tag`` table. Refer to
+ name of ``registered_model`` table.
+ """
+
+ tag = Column(String(256), nullable=False)
+ """
+ Registered model tag: `String` (limit 256 characters). Part of *Primary
Key* for
+ ``registered_model_tag`` table.
+ """
+
+ # linked entities
+ registered_model = relationship(
+ "SqlRegisteredModel", backref=backref("registered_model_tag",
cascade="all")
+ )
+
+ __table_args__ = (PrimaryKeyConstraint("name", "tag",
name="registered_model_tag_pk"),)
+
+ def __repr__(self):
+ return "<SqlRegisteredModelTag ({}, {})>".format(self.name, self.tag)
+
+ # entity mappers
+ def to_submarine_entity(self):
+ """
+ Convert DB model to corresponding submarine entity.
+ :return: :py:class:`submarine.entities.RegisteredModelTag`.
+ """
+ return RegisteredModelTag(self.tag)
+
+
+# +---------------------+---------+-----+-------------------------------+-----+
+# | name | version | ... | source | ... |
+# +---------------------+---------+-----+-------------------------------+-----+
+# | image_classfication | 1 | ... | s3://submarine/ResNet50/1/ | ... |
+# | image_classfication | 2 | ... | s3://submarine/DenseNet121/2/ | ... |
+# | speech_recoginition | 1 | ... | s3://submarine/ASR/1/ | ... |
+# +---------------------+---------+-----+-------------------------------+-----+
+
+
+class SqlModelVersion(Base):
+ __tablename__ = "model_version"
+
+ name = Column(
+ String(256), ForeignKey("registered_model.name", onupdate="cascade",
ondelete="cascade")
+ )
+ """
+ Name for registered models: Part of *Primary Key* for
``registered_model_tag`` table. Refer to
+ name of ``registered_model`` table.
+ """
+
+ version = Column(Integer, nullable=False)
+ """
+ Model version: Part of *Primary Key* for ``registered_model_tag`` table.
+ """
+
+ user_id = Column(String(64), nullable=False)
+ """
+ ID to whom this model is created
+ """
+
+ experiment_id = Column(String(64), nullable=False)
+ """
+ ID to which this model belongs to
+ """
+
+ current_stage = Column(String(20), default=STAGE_NONE)
+ """
+ Current stage of this model: it can be `None`, `Staging`, `Production` and
`Achieved`
+ """
+
+ creation_time = Column(DATETIME(fsp=3), default=datetime.now())
+ """
+ Creation time of this model version: default current time in milliseconds
+ """
+
+ last_updated_time = Column(DATETIME(fsp=3), nullable=True, default=None)
+ """
+ Last updated time of this model version
+ """
+
+ source = Column(String(512), nullable=True, default=None)
+ """
+ Source of model: database link refer to this model
+ """
+
+ dataset = Column(String(256), nullable=True, default=None)
+ """
+ Dataset used for this model.
+ """
+
+ description = Column(String(5000), nullable=True)
+ """
+ Description for model version.
+ """
+
+ # linked entities
+ registered_model = relationship(
+ "SqlRegisteredModel", backref=backref("model_version", cascade="all")
+ )
+
+ __table_args__ = (PrimaryKeyConstraint("name", "version",
name="model_version_pk"),)
+
+ def __repr__(self):
+ return "<SqlModelVersion ({}, {}, {}, {}, {}, {}, {}, {}, {},
{})>".format(
+ self.name,
+ self.version,
+ self.user_id,
+ self.experiment_id,
+ self.current_stage,
+ self.creation_time,
+ self.last_updated_time,
+ self.source,
+ self.dataset,
+ self.description,
+ )
+
+ def to_submarine_entity(self):
+ """
+ Convert DB model to corresponding Submarine entity.
+ :return: :py:class:`submarine.entities.RegisteredModel`.
+ """
+ return ModelVersion(
+ name=self.name,
+ version=self.version,
+ user_id=self.user_id,
+ experiment_id=self.experiment_id,
+ current_stage=self.current_stage,
+ creation_time=self.creation_time,
+ last_updated_time=self.last_updated_time,
+ source=self.source,
+ dataset=self.dataset,
+ description=self.description,
+ tags=[tag.to_submarine_entity for tag in self.model_tag],
+ )
+
+
+# +---------------------+---------+-----------------+
+# | name | version | tag |
+# +---------------------+---------+-----------------+
+# | image_classfication | 1 | best |
+# | image_classfication | 1 | anomaly_support |
+# | image_classfication | 2 | testing |
+# | speech_recoginition | 1 | best |
+# +---------------------+---------+-----------------+
+
+
+class SqlModelTag(Base):
+ __tablename__ = "model_tag"
+
+ name = Column(String(256), nullable=False)
+ """
+ Name for registered models: Part of *Foreign Key* for ``model_tag`` table.
Refer to
+ name of ``model_version`` table.
+ """
+
+ version = Column(Integer, nullable=False)
+ """
+ version of model: Part of *Foreign Key* for ``model_tag`` table. Refer to
+ version of ``model_version`` table.
+ """
+
+ tag = Column(String(256), nullable=False)
+ """
+ tag of model version: `String` (limit 256 characters). Part of *Primary
Key* for
+ ``model_tag`` table.
+ """
+
+ # linked entities
+ model_version = relationship(
+ "SqlModelVersion", foreign_keys=[name, version],
backref=backref("model_tag", cascade="all")
+ )
+
+ __table_args__ = (
+ PrimaryKeyConstraint("name", "tag", name="model_tag_pk"),
+ ForeignKeyConstraint(
+ ("name", "version"),
+ ("model_version.name", "model_version.version"),
+ onupdate="cascade",
+ ondelete="cascade",
+ ),
+ )
+
+ def __repr__(self):
+ return "<SqlRegisteredModelTag ({}, {}, {})>".format(self.name,
self.version, self.tag)
+
+ # entity mappers
+ def to_submarine_entity(self):
+ """
+ Convert DB model to corresponding submarine entity.
+ :return: :py:class:`submarine.entities.ModelTag`.
+ """
+ return ModelTag(self.tag)
-class SqlMetric(Base):
- __tablename__ = "metrics"
+
+#
+--------------------+-----------------+-----------+-------------------------+-----+
+# | id | experiment_spec | create_by | create_time
| ... |
+#
+--------------------+-----------------+-----------+-------------------------+-----+
+# | application_123456 | ... | root | 2021-08-30 10:10:10.100
| ... |
+#
+--------------------+-----------------+-----------+-------------------------+-----+
+
+
+class SqlExperiment(Base):
+ __tablename__ = "experiment"
id = Column(String(64))
"""
- ID to which this metric belongs to: Part of *Primary Key* for ``metrics``
table.
+ ID to which this metric belongs to: Part of *Primary Key* for
``experiment`` table.
+ """
+ experiment_spec = Column(Text)
+ """
+ The spec to create this experiment
+ """
+ create_by = Column(String(32))
+ """
+ This experiment is created by whom.
+ """
+ create_time = Column(DATETIME(fsp=3), default=datetime.now())
+ """
+ Datetime of this experiment be created
+ """
+ update_by = Column(String(32))
+ """
+ This experiment is created by whom.
+ """
+ update_time = Column(DATETIME(fsp=3))
+ """
+ Datetime of this experiment be updated
+ """
+
+ __table_args__ = (PrimaryKeyConstraint("id"),)
+
+ def __repr__(self):
+ return "<SqlMetric({}, {}, {}, {}, {}, {})>".format(
+ self.id,
+ self.experiment_spec,
+ self.create_by,
+ self.create_time,
+ self.update_by,
+ self.update_time,
+ )
+
+ def to_submarine_entity(self):
+ """
+ Convert DB model to corresponding Submarine entity.
+ :return: :py:class:`submarine.entities.Experiment`.
+ """
+ return Experiment(
+ id=self.id,
+ experiment_spec=self.experiment_spec,
+ create_by=self.create_by,
+ create_time=self.create_time,
+ update_by=self.update_by,
+ update_time=self.update_time,
+ )
+
+
+#
+--------------------+-------+-------------------+--------------+-------------------------+-----+
+# | id | key | value | worker_index | timestamp
| ... |
+#
+--------------------+-------+-------------------+--------------+-------------------------+-----+
+# | application_123456 | score | 0.666666666666667 | worker-1 | 2021-08-30
10:10:10.100 | ... |
+# | application_123456 | score | 0.666666668777777 | worker-1 | 2021-08-30
10:10:11.156 | ... |
+# | application_123456 | score | 0.666666670000001 | worker-1 | 2021-08-30
10:10:12.201 | ... |
+#
+--------------------+-------+-------------------+--------------+-------------------------+-----+
+
+
+class SqlMetric(Base):
+ __tablename__ = "metric"
+
+ id = Column(String(64), ForeignKey("experiment.id", onupdate="cascade",
ondelete="cascade"))
+ """
+ ID to which this metric belongs to: *Foreign Key* for ``experiment`` table.
+ Part of *Primary Key* for ``metric`` table.
"""
key = Column(String(190))
"""
- Metric key: `String` (limit 190 characters). Part of *Primary Key* for
``metrics`` table.
+ Metric key: `String` (limit 190 characters). Part of *Primary Key* for
``metric`` table.
"""
value = Column(sa.types.Float(precision=53), nullable=False)
"""
@@ -54,12 +395,12 @@ class SqlMetric(Base):
worker_index = Column(String(64))
"""
Metric worker_index: `String` (limit 32 characters). Part of *Primary Key*
for
- ``metrics`` table.
+ ``metric`` table.
"""
- timestamp = Column(BigInteger, default=lambda: int(time.time()))
+ timestamp = Column(DATETIME(fsp=3), default=datetime.now())
"""
- Timestamp recorded for this metric entry: `BigInteger`. Part of *Primary
Key* for
- ``metrics`` table.
+ Timestamp recorded for this metric entry: `DATETIME`. Part of *Primary
Key* for
+ ``metric`` table.
"""
step = Column(BigInteger, default=0, nullable=False)
"""
@@ -103,15 +444,16 @@ class SqlMetric(Base):
class SqlParam(Base):
- __tablename__ = "params"
+ __tablename__ = "param"
- id = Column(String(64))
+ id = Column(String(64), ForeignKey("experiment.id", onupdate="cascade",
ondelete="cascade"))
"""
- ID to which this parameter belongs to: Part of *Primary Key* for
``params`` table.
+ ID to which this parameter belongs to: *Foreign Key* for ``experiment``
table.
+ Part of *Primary Key* for ``param`` table.
"""
key = Column(String(190))
"""
- Param key: `String` (limit 190 characters). Part of *Primary Key* for
``params`` table.
+ Param key: `String` (limit 190 characters). Part of *Primary Key* for
``param`` table.
"""
value = Column(String(190), nullable=False)
"""
@@ -120,7 +462,7 @@ class SqlParam(Base):
worker_index = Column(String(32), nullable=False)
"""
Param worker_index: `String` (limit 32 characters). Part of *Primary Key*
for
- ``metrics`` table.
+ ``metric`` table.
"""
__table_args__ = (PrimaryKeyConstraint("id", "key", "worker_index",
name="param_pk"),)
diff --git a/submarine-sdk/pysubmarine/submarine/tracking/fluent.py
b/submarine-sdk/pysubmarine/submarine/tracking/fluent.py
index f7ee031..8406ce7 100644
--- a/submarine-sdk/pysubmarine/submarine/tracking/fluent.py
+++ b/submarine-sdk/pysubmarine/submarine/tracking/fluent.py
@@ -19,7 +19,7 @@ Submarine run. This module is exposed to users at the
top-level :py:mod:`submari
from __future__ import print_function
import logging
-import time
+from datetime import datetime
from submarine.tracking.client import SubmarineClient
from submarine.tracking.utils import get_job_id, get_worker_index
@@ -51,6 +51,4 @@ def log_metric(key, value, step=None):
"""
job_id = get_job_id()
worker_index = get_worker_index()
- SubmarineClient().log_metric(
- job_id, key, value, worker_index, int(time.time() * 1000), step or 0
- )
+ SubmarineClient().log_metric(job_id, key, value, worker_index,
datetime.now(), step or 0)
diff --git a/submarine-sdk/pysubmarine/submarine/utils/validation.py
b/submarine-sdk/pysubmarine/submarine/utils/validation.py
index 37e5766..60dd9b0 100644
--- a/submarine-sdk/pysubmarine/submarine/utils/validation.py
+++ b/submarine-sdk/pysubmarine/submarine/utils/validation.py
@@ -18,6 +18,7 @@ Utilities for validating user inputs such as metric names and
parameter names.
import numbers
import posixpath
import re
+from datetime import datetime
from submarine.exceptions import SubmarineException
from submarine.store.database.db_types import DATABASE_ENGINES
@@ -92,10 +93,10 @@ def validate_metric(key, value, timestamp, step):
"double (64-bit floating point)" % (value, key, timestamp),
)
- if not isinstance(timestamp, numbers.Number) or timestamp < 0:
+ if not isinstance(timestamp, datetime):
raise SubmarineException(
- "Got invalid timestamp %s for metric '%s' (value=%s). Timestamp
must be a nonnegative "
- "long (64-bit integer) " % (timestamp, key, value),
+ "Got invalid timestamp %s for metric '%s' (value=%s). Timestamp
must be a datetime "
+ "object." % (timestamp, key, value),
)
if not isinstance(step, numbers.Number):
diff --git
a/submarine-sdk/pysubmarine/tests/entities/model_registry/test_model_version.py
b/submarine-sdk/pysubmarine/tests/entities/model_registry/test_model_version.py
new file mode 100644
index 0000000..6611fb4
--- /dev/null
+++
b/submarine-sdk/pysubmarine/tests/entities/model_registry/test_model_version.py
@@ -0,0 +1,125 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import datetime
+
+from submarine.entities.model_registry.model_tag import ModelTag
+from submarine.entities.model_registry.model_version import ModelVersion
+from submarine.entities.model_registry.model_version_stages import STAGE_NONE
+
+
+class TestModelVersion:
+ default_data = {
+ "name": "test",
+ "version": 1,
+ "user_id": "admin",
+ "experiment_id": "experiment_1",
+ "current_stage": STAGE_NONE,
+ "creation_time": datetime.now(),
+ "last_updated_time": datetime.now(),
+ "source": "path/to/source",
+ "dataset": "test",
+ "description": "registered model description",
+ "tags": [],
+ }
+
+ def _check(
+ self,
+ model_version,
+ name,
+ version,
+ user_id,
+ experiment_id,
+ current_stage,
+ creation_time,
+ last_updated_time,
+ source,
+ dataset,
+ description,
+ tags,
+ ):
+ isinstance(model_version, ModelVersion)
+ assert model_version.name == name
+ assert model_version.version == version
+ assert model_version.user_id == user_id
+ assert model_version.experiment_id == experiment_id
+ assert model_version.current_stage == current_stage
+ assert model_version.creation_time == creation_time
+ assert model_version.last_updated_time == last_updated_time
+ assert model_version.source == source
+ assert model_version.dataset == dataset
+ assert model_version.description == description
+ assert model_version.tags == tags
+
+ def test_creation_and_hydration(self):
+ mv = ModelVersion(
+ self.default_data["name"],
+ self.default_data["version"],
+ self.default_data["user_id"],
+ self.default_data["experiment_id"],
+ self.default_data["current_stage"],
+ self.default_data["creation_time"],
+ self.default_data["last_updated_time"],
+ self.default_data["source"],
+ self.default_data["dataset"],
+ self.default_data["description"],
+ self.default_data["tags"],
+ )
+ self._check(
+ mv,
+ self.default_data["name"],
+ self.default_data["version"],
+ self.default_data["user_id"],
+ self.default_data["experiment_id"],
+ self.default_data["current_stage"],
+ self.default_data["creation_time"],
+ self.default_data["last_updated_time"],
+ self.default_data["source"],
+ self.default_data["dataset"],
+ self.default_data["description"],
+ self.default_data["tags"],
+ )
+
+ def test_with_tags(self):
+ tag1 = ModelTag("tag1")
+ tag2 = ModelTag("tag2")
+ tags = [tag1, tag2]
+ mv = ModelVersion(
+ self.default_data["name"],
+ self.default_data["version"],
+ self.default_data["user_id"],
+ self.default_data["experiment_id"],
+ self.default_data["current_stage"],
+ self.default_data["creation_time"],
+ self.default_data["last_updated_time"],
+ self.default_data["source"],
+ self.default_data["dataset"],
+ self.default_data["description"],
+ tags,
+ )
+ self._check(
+ mv,
+ self.default_data["name"],
+ self.default_data["version"],
+ self.default_data["user_id"],
+ self.default_data["experiment_id"],
+ self.default_data["current_stage"],
+ self.default_data["creation_time"],
+ self.default_data["last_updated_time"],
+ self.default_data["source"],
+ self.default_data["dataset"],
+ self.default_data["description"],
+ [t.tag for t in tags],
+ )
diff --git
a/submarine-sdk/pysubmarine/tests/entities/model_registry/test_registered_model.py
b/submarine-sdk/pysubmarine/tests/entities/model_registry/test_registered_model.py
new file mode 100644
index 0000000..e1fa2af
--- /dev/null
+++
b/submarine-sdk/pysubmarine/tests/entities/model_registry/test_registered_model.py
@@ -0,0 +1,74 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import datetime
+
+from submarine.entities.model_registry.registered_model import RegisteredModel
+from submarine.entities.model_registry.registered_model_tag import
RegisteredModelTag
+
+
+class TestRegisteredModel:
+ default_data = {
+ "name": "test",
+ "creation_time": datetime.now(),
+ "last_updated_time": datetime.now(),
+ "description": "registered model description",
+ "tags": [],
+ }
+
+ def _check(self, registered_model, name, creation_time, last_updated_time,
description, tags):
+ isinstance(registered_model, RegisteredModel)
+ assert registered_model.name == name
+ assert registered_model.creation_time == creation_time
+ assert registered_model.last_updated_time == last_updated_time
+ assert registered_model.description == description
+ assert registered_model.tags == tags
+
+ def test_creation_and_hydration(self):
+ rm = RegisteredModel(
+ self.default_data["name"],
+ self.default_data["creation_time"],
+ self.default_data["last_updated_time"],
+ self.default_data["description"],
+ self.default_data["tags"],
+ )
+ self._check(
+ rm,
+ self.default_data["name"],
+ self.default_data["creation_time"],
+ self.default_data["last_updated_time"],
+ self.default_data["description"],
+ self.default_data["tags"],
+ )
+
+ def test_with_tags(self):
+ tag1 = RegisteredModelTag("tag1")
+ tag2 = RegisteredModelTag("tag2")
+ tags = [tag1, tag2]
+ rm = RegisteredModel(
+ self.default_data["name"],
+ self.default_data["creation_time"],
+ self.default_data["last_updated_time"],
+ self.default_data["description"],
+ tags,
+ )
+ self._check(
+ rm,
+ self.default_data["name"],
+ self.default_data["creation_time"],
+ self.default_data["last_updated_time"],
+ self.default_data["description"],
+ [t.tag for t in tags],
+ )
diff --git a/submarine-sdk/pysubmarine/tests/entities/test_metrics.py
b/submarine-sdk/pysubmarine/tests/entities/test_metrics.py
index c8a62e4..8e09de1 100644
--- a/submarine-sdk/pysubmarine/tests/entities/test_metrics.py
+++ b/submarine-sdk/pysubmarine/tests/entities/test_metrics.py
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import time
+from datetime import datetime
from submarine.entities import Metric
@@ -31,7 +31,7 @@ def test_creation_and_hydration():
key = "alpha"
value = 10000
worker_index = 1
- ts = int(time.time())
+ ts = datetime.now()
step = 0
metric = Metric(key, value, worker_index, ts, step)
diff --git a/submarine-sdk/pysubmarine/tests/store/test_sqlalchemy_store.py
b/submarine-sdk/pysubmarine/tests/store/test_sqlalchemy_store.py
index f30dcd7..d7597d8 100644
--- a/submarine-sdk/pysubmarine/tests/store/test_sqlalchemy_store.py
+++ b/submarine-sdk/pysubmarine/tests/store/test_sqlalchemy_store.py
@@ -13,15 +13,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import time
import unittest
+from datetime import datetime
import pytest
import submarine
from submarine.entities import Metric, Param
from submarine.store.database import models
-from submarine.store.database.models import SqlMetric, SqlParam
+from submarine.store.database.models import SqlExperiment, SqlMetric, SqlParam
from submarine.tracking import utils
JOB_ID = "application_123456789"
@@ -35,6 +35,18 @@ class TestSqlAlchemyStore(unittest.TestCase):
)
self.tracking_uri = utils.get_tracking_uri()
self.store = utils.get_sqlalchemy_store(self.tracking_uri)
+ # TODO: use submarine.tracking.fluent to support experiment create
+ with self.store.ManagedSessionMaker() as session:
+ instance = SqlExperiment(
+ id=JOB_ID,
+ experiment_spec='{"value": 1}',
+ create_by="test",
+ create_time=datetime.now(),
+ update_by=None,
+ update_time=None,
+ )
+ session.add(instance)
+ session.commit()
def tearDown(self):
submarine.set_tracking_uri(None)
@@ -53,8 +65,8 @@ class TestSqlAlchemyStore(unittest.TestCase):
assert params[0].id == JOB_ID
def test_log_metric(self):
- metric1 = Metric("name_1", 5, "worker-1", int(time.time()), 0)
- metric2 = Metric("name_1", 6, "worker-2", int(time.time()), 0)
+ metric1 = Metric("name_1", 5, "worker-1", datetime.now(), 0)
+ metric2 = Metric("name_1", 6, "worker-2", datetime.now(), 0)
self.store.log_metric(JOB_ID, metric1)
self.store.log_metric(JOB_ID, metric2)
diff --git a/submarine-sdk/pysubmarine/tests/tracking/test_tracking.py
b/submarine-sdk/pysubmarine/tests/tracking/test_tracking.py
index 7058741..e167a1c 100644
--- a/submarine-sdk/pysubmarine/tests/tracking/test_tracking.py
+++ b/submarine-sdk/pysubmarine/tests/tracking/test_tracking.py
@@ -14,13 +14,14 @@
# limitations under the License.
import unittest
+from datetime import datetime
from os import environ
import pytest
import submarine
from submarine.store.database import models
-from submarine.store.database.models import SqlMetric, SqlParam
+from submarine.store.database.models import SqlExperiment, SqlMetric, SqlParam
from submarine.tracking import utils
JOB_ID = "application_123456789"
@@ -35,12 +36,24 @@ class TestTracking(unittest.TestCase):
)
self.tracking_uri = utils.get_tracking_uri()
self.store = utils.get_sqlalchemy_store(self.tracking_uri)
+ # TODO: use submarine.tracking.fluent to support experiment create
+ with self.store.ManagedSessionMaker() as session:
+ instance = SqlExperiment(
+ id=JOB_ID,
+ experiment_spec='{"value": 1}',
+ create_by="test",
+ create_time=datetime.now(),
+ update_by=None,
+ update_time=None,
+ )
+ session.add(instance)
+ session.commit()
def tearDown(self):
submarine.set_tracking_uri(None)
models.Base.metadata.drop_all(self.store.engine)
- def log_param(self):
+ def test_log_param(self):
submarine.log_param("name_1", "a")
# Validate params
with self.store.ManagedSessionMaker() as session:
diff --git
a/submarine-server/server-core/src/main/java/org/apache/submarine/server/workbench/database/entity/Metric.java
b/submarine-server/server-core/src/main/java/org/apache/submarine/server/workbench/database/entity/Metric.java
index ae3112d..258e074 100644
---
a/submarine-server/server-core/src/main/java/org/apache/submarine/server/workbench/database/entity/Metric.java
+++
b/submarine-server/server-core/src/main/java/org/apache/submarine/server/workbench/database/entity/Metric.java
@@ -18,10 +18,10 @@
*/
package org.apache.submarine.server.workbench.database.entity;
-import java.math.BigInteger;
-
import org.apache.submarine.server.database.entity.BaseEntity;
+import java.sql.Timestamp;
+
/*
#
+--------------------+-------+-------------------+--------------+---------------+------+--------+
# | id | key | value | worker_index | timestamp
| step | is_nan |
@@ -38,7 +38,7 @@ public class Metric extends BaseEntity {
private String key;
private Float value;
private String workerIndex;
- private BigInteger timestamp;
+ private Timestamp timestamp;
private Integer step;
private Boolean isNan;
@@ -66,11 +66,11 @@ public class Metric extends BaseEntity {
this.workerIndex = workerIndex;
}
- public BigInteger getTimestamp() {
+ public Timestamp getTimestamp() {
return this.timestamp;
}
- public void setTimestamp(BigInteger timestamp) {
+ public void setTimestamp(Timestamp timestamp) {
this.timestamp = timestamp;
}
diff --git
a/submarine-server/server-core/src/main/java/org/apache/submarine/server/workbench/rest/MetricRestApi.java
b/submarine-server/server-core/src/main/java/org/apache/submarine/server/workbench/rest/MetricRestApi.java
index 26cb50e..d3a60e2 100644
---
a/submarine-server/server-core/src/main/java/org/apache/submarine/server/workbench/rest/MetricRestApi.java
+++
b/submarine-server/server-core/src/main/java/org/apache/submarine/server/workbench/rest/MetricRestApi.java
@@ -18,16 +18,13 @@
*/
package org.apache.submarine.server.workbench.rest;
+import org.apache.submarine.server.response.JsonResponse;
import org.apache.submarine.server.workbench.annotation.SubmarineApi;
import org.apache.submarine.server.workbench.database.entity.Metric;
import org.apache.submarine.server.workbench.database.service.MetricService;
-import org.apache.submarine.server.response.JsonResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.math.BigInteger;
-import java.util.List;
-
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.DELETE;
@@ -39,6 +36,8 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
+import java.sql.Timestamp;
+import java.util.List;
@Path("/metric")
@Produces("application/json")
@@ -57,7 +56,7 @@ public class MetricRestApi {
public Response listMetric(@QueryParam("metricKey") String metricKey,
@QueryParam("value") Float value,
@QueryParam("workerIndex") String workerIndex,
- @QueryParam("timestamp") BigInteger timestamp,
+ @QueryParam("timestamp") Timestamp timestamp,
@QueryParam("step") Integer step,
@QueryParam("isNan") Boolean isNan,
@QueryParam("id") String id) {
diff --git
a/submarine-server/server-core/src/main/resources/org/apache/submarine/database/mappers/MetricMapper.xml
b/submarine-server/server-core/src/main/resources/org/apache/submarine/database/mappers/MetricMapper.xml
index 9922019..5fc85fa 100644
---
a/submarine-server/server-core/src/main/resources/org/apache/submarine/database/mappers/MetricMapper.xml
+++
b/submarine-server/server-core/src/main/resources/org/apache/submarine/database/mappers/MetricMapper.xml
@@ -27,7 +27,7 @@
<result column="key" jdbcType="VARCHAR" property="key" />
<result column="value" jdbcType="FLOAT" property="value" />
<result column="worker_index" jdbcType="VARCHAR" property="workerIndex" />
- <result column="timestamp" jdbcType="BIGINT" property="timestamp" />
+ <result column="timestamp" jdbcType="TIMESTAMP" property="timestamp" />
<result column="step" jdbcType="INTEGER" property="step" />
<result column="is_nan" jdbcType="BOOLEAN" property="isNan" />
</resultMap>
@@ -40,40 +40,40 @@
<select id="selectAll" parameterType="java.lang.String"
resultMap="resultMap">
select
<include refid="Base_Column_List" />
- from metrics
+ from metric
where 1 = 1
</select>
<select id="selectById" parameterType="java.lang.String"
resultMap="resultMap">
select
<include refid="Base_Column_List" />
- from metrics
+ from metric
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteById" parameterType="java.lang.String">
- delete from metrics
+ delete from metric
where id = #{id,jdbcType=VARCHAR}
</delete>
<insert id="insert"
parameterType="org.apache.submarine.server.workbench.database.entity.Metric"
keyProperty="id, key, worker_index, timestamp">
- insert into metrics (id, `key`, value, worker_index, timestamp, step,
is_nan)
+ insert into metric (id, `key`, value, worker_index, timestamp, step,
is_nan)
values (#{id,jdbcType=VARCHAR},
#{key,jdbcType=VARCHAR},
#{value,jdbcType=FLOAT},
#{workerIndex,jdbcType=VARCHAR},
- #{timestamp,jdbcType=BIGINT},
+ #{timestamp,jdbcType=TIMESTAMP},
#{step,jdbcType=INTEGER},
#{isNan,jdbcType=BOOLEAN})
</insert>
<update id="update"
parameterType="org.apache.submarine.server.workbench.database.entity.Metric">
- update metrics
+ update metric
set `key` = #{key,jdbcType=VARCHAR},
value = #{value,jdbcType=FLOAT},
worker_index = #{workerIndex,jdbcType=VARCHAR},
- timestamp = #{timestamp,jdbcType=BIGINT},
+ timestamp = #{timestamp,jdbcType=TIMESTAMP},
step = #{step,jdbcType=INTEGER},
is_nan = #{isNan,jdbcType=BOOLEAN}
where id = #{id,jdbcType=VARCHAR}
@@ -82,7 +82,7 @@
<select id="selectByPrimaryKeySelective" parameterType="java.lang.String"
resultMap="resultMap">
select
<include refid="Base_Column_List" />
- from metrics
+ from metric
where 1 = 1
<if test="key != null">
AND `key` = #{key,jdbcType=VARCHAR}
@@ -91,7 +91,7 @@
AND worker_index = #{workerIndex,jdbcType=VARCHAR}
</if>
<if test="timestamp != null">
- AND timestamp = #{timestamp,jdbcType=BIGINT}
+ AND timestamp = #{timestamp,jdbcType=TIMESTAMP}
</if>
<if test="step != null">
AND step = #{step,jdbcType=INTEGER}
diff --git
a/submarine-server/server-core/src/main/resources/org/apache/submarine/database/mappers/ParamMapper.xml
b/submarine-server/server-core/src/main/resources/org/apache/submarine/database/mappers/ParamMapper.xml
index 4e97759..dd76329 100644
---
a/submarine-server/server-core/src/main/resources/org/apache/submarine/database/mappers/ParamMapper.xml
+++
b/submarine-server/server-core/src/main/resources/org/apache/submarine/database/mappers/ParamMapper.xml
@@ -37,30 +37,30 @@
<select id="selectAll" parameterType="java.lang.String"
resultMap="resultMap">
select
<include refid="Base_Column_List" />
- from params
+ from param
where 1 = 1
</select>
<select id="selectById" parameterType="java.lang.String"
resultMap="resultMap">
select
<include refid="Base_Column_List" />
- from params
+ from param
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteById" parameterType="java.lang.String">
- delete from params
+ delete from param
where id = #{id,jdbcType=VARCHAR}
</delete>
<insert id="insert"
parameterType="org.apache.submarine.server.workbench.database.entity.Param"
keyProperty="id, key, worker_index">
- insert into params (id, `key`, value, worker_index)
+ insert into param (id, `key`, value, worker_index)
values (#{id,jdbcType=VARCHAR}, #{key,jdbcType=VARCHAR},
#{value,jdbcType=FLOAT}, #{workerIndex,jdbcType=VARCHAR})
</insert>
<update id="update"
parameterType="org.apache.submarine.server.workbench.database.entity.Param">
- update params
+ update param
set `key` = #{key,jdbcType=VARCHAR},
value = #{value,jdbcType=FLOAT},
worker_index = #{workerIndex,jdbcType=VARCHAR}
@@ -70,7 +70,7 @@
<select id="selectByPrimaryKeySelective" parameterType="java.lang.String"
resultMap="resultMap">
select
<include refid="Base_Column_List" />
- from params
+ from param
where 1 = 1
<if test="key != null">
AND `key` = #{key,jdbcType=VARCHAR}
diff --git
a/submarine-server/server-core/src/test/java/org/apache/submarine/server/workbench/database/service/MetricServiceTest.java
b/submarine-server/server-core/src/test/java/org/apache/submarine/server/workbench/database/service/MetricServiceTest.java
index a919acf..9798151 100644
---
a/submarine-server/server-core/src/test/java/org/apache/submarine/server/workbench/database/service/MetricServiceTest.java
+++
b/submarine-server/server-core/src/test/java/org/apache/submarine/server/workbench/database/service/MetricServiceTest.java
@@ -18,13 +18,17 @@
*/
package org.apache.submarine.server.workbench.database.service;
+import org.apache.submarine.server.experiment.database.ExperimentEntity;
+import org.apache.submarine.server.experiment.database.ExperimentService;
import org.apache.submarine.server.workbench.database.entity.Metric;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.util.Date;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
@@ -33,6 +37,20 @@ import static org.junit.Assert.assertTrue;
public class MetricServiceTest {
private static final Logger LOG =
LoggerFactory.getLogger(MetricServiceTest.class);
MetricService metricService = new MetricService();
+ ExperimentService experimentService = new ExperimentService();
+
+ // Id of metric is a foreign key for experiment id so experiment must be
created before test.
+ @Before
+ public void createExperiment() {
+ ExperimentEntity entity = new ExperimentEntity();
+ String id = "test_application_1234";
+ String spec = "{\"value\": 1}";
+
+ entity.setId(id);
+ entity.setExperimentSpec(spec);
+
+ experimentService.insert(entity);
+ }
@After
public void removeAllRecord() throws Exception {
@@ -41,16 +59,20 @@ public class MetricServiceTest {
for (Metric metric : metricList) {
metricService.deleteById(metric.getId());
}
+
+ experimentService.selectAll().forEach(e ->
experimentService.delete(e.getId()));
}
@Test
public void testSelect() throws Exception {
+ Timestamp timestamp = new Timestamp(new Date().getTime());
+
Metric metric = new Metric();
metric.setId("test_application_1234");
metric.setKey("test_score");
metric.setValue((float) 0.666667);
metric.setWorkerIndex("test_worker-1");
- metric.setTimestamp(new BigInteger("1569139525097"));
+ metric.setTimestamp(timestamp);
metric.setStep(0);
metric.setIsNan(false);
boolean result = metricService.insert(metric);
@@ -68,22 +90,26 @@ public class MetricServiceTest {
@Test
public void testUpdate() throws Exception {
+ Timestamp timestamp = new Timestamp(new Date().getTime());
+
Metric metric = new Metric();
metric.setId("test_application_1234");
metric.setKey("test_score");
metric.setValue((float) 0.666667);
metric.setWorkerIndex("test_worker-2");
- metric.setTimestamp(new BigInteger("1569139525098"));
+ metric.setTimestamp(timestamp);
metric.setStep(0);
metric.setIsNan(false);
boolean result = metricService.insert(metric);
assertTrue(result);
+ Timestamp nextTimestamp = new Timestamp(new Date().getTime());
+
metric.setId("test_application_1234");
metric.setKey("test_scoreNew");
metric.setValue((float) 0.766667);
metric.setWorkerIndex("test_worker-New");
- metric.setTimestamp(new BigInteger("2569139525098"));
+ metric.setTimestamp(nextTimestamp);
metric.setStep(1);
metric.setIsNan(true);
@@ -96,12 +122,14 @@ public class MetricServiceTest {
@Test
public void testDelete() throws Exception {
+ Timestamp timestamp = new Timestamp(new Date().getTime());
+
Metric metric = new Metric();
metric.setId("test_application_1234");
metric.setKey("test_score");
metric.setValue((float) 0.666667);
metric.setWorkerIndex("test_worker-2");
- metric.setTimestamp(new BigInteger("1569139525098"));
+ metric.setTimestamp(timestamp);
metric.setStep(0);
metric.setIsNan(false);
boolean result = metricService.insert(metric);
diff --git
a/submarine-server/server-core/src/test/java/org/apache/submarine/server/workbench/database/service/ParamServiceTest.java
b/submarine-server/server-core/src/test/java/org/apache/submarine/server/workbench/database/service/ParamServiceTest.java
index 2cbe1be..1904329 100644
---
a/submarine-server/server-core/src/test/java/org/apache/submarine/server/workbench/database/service/ParamServiceTest.java
+++
b/submarine-server/server-core/src/test/java/org/apache/submarine/server/workbench/database/service/ParamServiceTest.java
@@ -18,8 +18,11 @@
*/
package org.apache.submarine.server.workbench.database.service;
+import org.apache.submarine.server.experiment.database.ExperimentEntity;
+import org.apache.submarine.server.experiment.database.ExperimentService;
import org.apache.submarine.server.workbench.database.entity.Param;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,7 +35,19 @@ import static org.junit.Assert.assertTrue;
public class ParamServiceTest {
private static final Logger LOG =
LoggerFactory.getLogger(ParamServiceTest.class);
ParamService paramService = new ParamService();
+ ExperimentService experimentService = new ExperimentService();
+ @Before
+ public void createExperiment() throws Exception {
+ ExperimentEntity entity = new ExperimentEntity();
+ String id = "test_application_12345";
+ String spec = "{\"value\": 1}";
+
+ entity.setId(id);
+ entity.setExperimentSpec(spec);
+
+ experimentService.insert(entity);
+ }
@After
public void removeAllRecord() throws Exception {
List<Param> paramList = paramService.selectAll();
@@ -40,12 +55,14 @@ public class ParamServiceTest {
for (Param param : paramList) {
paramService.deleteById(param.getId());
}
+
+ experimentService.selectAll().forEach(e ->
experimentService.delete(e.getId()));
}
@Test
public void testSelect() throws Exception {
Param param = new Param();
- param.setId("test_application_1234");
+ param.setId("test_application_12345");
param.setKey("test_score");
param.setValue("199");
param.setWorkerIndex("test_worker-1");
@@ -65,7 +82,7 @@ public class ParamServiceTest {
@Test
public void testUpdate() throws Exception {
Param param = new Param();
- param.setId("test_application_1234");
+ param.setId("test_application_12345");
param.setKey("test_score");
param.setValue("100");
param.setWorkerIndex("test_worker-2");
@@ -85,7 +102,7 @@ public class ParamServiceTest {
@Test
public void testDelete() throws Exception {
Param param = new Param();
- param.setId("test_application_1234");
+ param.setId("test_application_12345");
param.setKey("test_score");
param.setValue("100");
param.setWorkerIndex("test_worker-2");
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]