TensorFlow Serving C++ API Documentation
server_core_test.cc
1 /* Copyright 2016 Google Inc. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7  http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow_serving/model_servers/server_core.h"
17 
18 #include <map>
19 #include <memory>
20 #include <unordered_map>
21 #include <utility>
22 #include <vector>
23 
24 #include "google/protobuf/any.pb.h"
25 #include "absl/status/status.h"
26 #include "absl/strings/strip.h"
27 #include "tensorflow/cc/saved_model/tag_constants.h"
28 #include "xla/tsl/lib/core/status_test_util.h"
29 #include "tensorflow/core/lib/core/status.h"
30 #include "tensorflow/core/lib/core/status_test_util.h"
31 #include "tensorflow/core/lib/io/path.h"
32 #include "tensorflow/core/lib/random/random.h"
33 #include "tensorflow/core/lib/strings/stringprintf.h"
34 #include "tensorflow/core/platform/path.h"
35 #include "tensorflow/core/platform/types.h"
36 #include "tensorflow/core/protobuf/error_codes.pb.h"
37 #include "tensorflow_serving/apis/model.pb.h"
38 #include "tensorflow_serving/apis/predict.pb.h"
39 #include "tensorflow_serving/core/request_logger.h"
40 #include "tensorflow_serving/core/servable_handle.h"
41 #include "tensorflow_serving/core/servable_id.h"
42 #include "tensorflow_serving/core/servable_state.h"
43 #include "tensorflow_serving/core/test_util/availability_test_util.h"
44 #include "tensorflow_serving/core/test_util/fake_loader_source_adapter.pb.h"
45 #include "tensorflow_serving/core/test_util/fake_log_collector.h"
46 #include "tensorflow_serving/core/test_util/mock_prediction_stream_logger.h"
47 #include "tensorflow_serving/core/test_util/mock_request_logger.h"
48 #include "tensorflow_serving/model_servers/model_platform_types.h"
49 #include "tensorflow_serving/model_servers/test_util/server_core_test_util.h"
50 #include "tensorflow_serving/model_servers/test_util/storage_path_error_injecting_source_adapter.h"
51 #include "tensorflow_serving/model_servers/test_util/storage_path_error_injecting_source_adapter.pb.h"
52 #include "tensorflow_serving/test_util/test_util.h"
53 #include "tensorflow_serving/util/oss_or_google.h"
54 
55 namespace tensorflow {
56 namespace serving {
57 namespace {
58 
59 using test_util::MockPredictionStreamLogger;
60 using test_util::ServerCoreTest;
61 using ::testing::_;
62 using ::testing::Invoke;
63 using ::testing::MockFunction;
64 using ::testing::NiceMock;
65 using ::testing::Pair;
66 using ::testing::UnorderedElementsAre;
67 
68 TEST_P(ServerCoreTest, PreLoadHook) {
69  std::unique_ptr<ServerCore> server_core;
70  ServerCore::Options options = GetDefaultOptions();
71  MockFunction<void(const ServableId&)> mock_pre_load_hook;
72  options.pre_load_hook = mock_pre_load_hook.AsStdFunction();
73  options.model_server_config = GetTestModelServerConfigForFakePlatform();
74 
75  const ServableId expected_id = {test_util::kTestModelName,
76  test_util::kTestModelVersion};
77  EXPECT_CALL(mock_pre_load_hook, Call(expected_id));
78  TF_ASSERT_OK(ServerCore::Create(std::move(options), &server_core));
79 }
80 
81 TEST_P(ServerCoreTest, CreateWaitsTillModelsAvailable) {
82  std::unique_ptr<ServerCore> server_core;
83  TF_ASSERT_OK(CreateServerCore(GetTestModelServerConfigForFakePlatform(),
84  &server_core));
85 
86  const std::vector<ServableId> available_servables =
87  server_core->ListAvailableServableIds();
88  ASSERT_EQ(available_servables.size(), 1);
89  const ServableId expected_id = {test_util::kTestModelName,
90  test_util::kTestModelVersion};
91  EXPECT_EQ(available_servables.at(0), expected_id);
92 
93  ModelSpec model_spec;
94  model_spec.set_name(test_util::kTestModelName);
95  model_spec.mutable_version()->set_value(test_util::kTestModelVersion);
96  ServableHandle<string> servable_handle;
97  TF_ASSERT_OK(
98  server_core->GetServableHandle<string>(model_spec, &servable_handle));
99  EXPECT_EQ(servable_handle.id(), expected_id);
100 
101  // Validate monitoring states.
102  const auto servable_map =
103  server_core->servable_state_monitor()->GetAllServableStates();
104  auto it_servable = servable_map.find(test_util::kTestModelName);
105  ASSERT_NE(it_servable, servable_map.end());
106  ASSERT_THAT(it_servable->second,
107  UnorderedElementsAre(Pair(test_util::kTestModelVersion, _)));
108  const auto state_and_time =
109  it_servable->second.at(test_util::kTestModelVersion);
110  EXPECT_TRUE(state_and_time.state.health.ok());
111  EXPECT_EQ(state_and_time.state.manager_state,
112  ServableState::ManagerState::kAvailable);
113 }
114 
115 TEST_P(ServerCoreTest, ReloadConfigWaitsTillModelsAvailable) {
116  // Create a server with no models, initially.
117  std::unique_ptr<ServerCore> server_core;
118  TF_ASSERT_OK(CreateServerCore(ModelServerConfig(), &server_core));
119 
120  // Reconfigure it to load our test model.
121  TF_ASSERT_OK(
122  server_core->ReloadConfig(GetTestModelServerConfigForFakePlatform()));
123 
124  const std::vector<ServableId> available_servables =
125  server_core->ListAvailableServableIds();
126  ASSERT_EQ(available_servables.size(), 1);
127  const ServableId expected_id = {test_util::kTestModelName,
128  test_util::kTestModelVersion};
129  EXPECT_EQ(available_servables.at(0), expected_id);
130 }
131 
132 TEST_P(ServerCoreTest, ReloadConfigUnloadsModels) {
133  const ModelServerConfig nonempty_config =
134  GetTestModelServerConfigForFakePlatform();
135  ModelServerConfig empty_config;
136  empty_config.mutable_model_config_list();
137  const ServableId servable_id = {test_util::kTestModelName,
138  test_util::kTestModelVersion};
139 
140  std::unique_ptr<ServerCore> server_core;
141  TF_ASSERT_OK(CreateServerCore(nonempty_config, &server_core));
142  ASSERT_FALSE(server_core->ListAvailableServableIds().empty());
143 
144  TF_ASSERT_OK(server_core->ReloadConfig(empty_config));
145  // Wait for the unload to finish (ReloadConfig() doesn't block on this).
146  test_util::WaitUntilServableManagerStateIsOneOf(
147  *server_core->servable_state_monitor(), servable_id,
148  {ServableState::ManagerState::kEnd});
149  // Validate monitoring states.
150  const auto servable_map =
151  server_core->servable_state_monitor()->GetAllServableStates();
152  auto it_servable = servable_map.find(test_util::kTestModelName);
153  ASSERT_NE(it_servable, servable_map.end());
154  ASSERT_THAT(it_servable->second,
155  UnorderedElementsAre(Pair(test_util::kTestModelVersion, _)));
156  const auto state_and_time =
157  it_servable->second.at(test_util::kTestModelVersion);
158  ASSERT_EQ(state_and_time.state.manager_state,
159  ServableState::ManagerState::kEnd);
160 }
161 
162 TEST_P(ServerCoreTest, ReloadConfigHandlesLoadingAPreviouslyUnloadedModel) {
163  ModelServerConfig empty_config;
164  empty_config.mutable_model_config_list();
165  const ModelServerConfig nonempty_config =
166  GetTestModelServerConfigForFakePlatform();
167  const ServableId servable_id = {test_util::kTestModelName,
168  test_util::kTestModelVersion};
169 
170  // Load, and then unload, a servable.
171  std::unique_ptr<ServerCore> server_core;
172  TF_ASSERT_OK(CreateServerCore(nonempty_config, &server_core));
173  TF_ASSERT_OK(server_core->ReloadConfig(empty_config));
174  // Wait for the unload to finish (ReloadConfig() doesn't block on this).
175  test_util::WaitUntilServableManagerStateIsOneOf(
176  *server_core->servable_state_monitor(), servable_id,
177  {ServableState::ManagerState::kEnd});
178 
179  // Re-load the same servable.
180  TF_ASSERT_OK(server_core->ReloadConfig(nonempty_config));
181  const std::vector<ServableId> available_servables =
182  server_core->ListAvailableServableIds();
183  ASSERT_EQ(available_servables.size(), 1);
184  const ServableId expected_id = {test_util::kTestModelName,
185  test_util::kTestModelVersion};
186  EXPECT_EQ(available_servables.at(0), expected_id);
187 }
188 
189 TEST_P(ServerCoreTest, ReloadConfigChangeModelBasePath) {
190  // Create two configs that differ only in the model's base path. One base path
191  // has a single version test_util::kTestModelVersion, and one has two versions
192  // test_util::kTestModelVersion and test_util::kTestModelLargerVersion.
193  const ModelServerConfig one_version_config =
194  GetTestModelServerConfigForFakePlatform();
195  ModelServerConfig two_version_config =
196  GetTestModelServerConfigForFakePlatform();
197  SwitchToHalfPlusTwoWith2Versions(&two_version_config);
198 
199  // Start with the one-version path.
200  std::unique_ptr<ServerCore> server_core;
201  TF_ASSERT_OK(CreateServerCore(one_version_config, &server_core));
202  std::vector<ServableId> available_servables =
203  server_core->ListAvailableServableIds();
204  ASSERT_EQ(1, available_servables.size());
205  EXPECT_EQ(test_util::kTestModelVersion, available_servables.at(0).version);
206 
207  // Switch to the two-version path.
208  TF_ASSERT_OK(server_core->ReloadConfig(two_version_config));
209  // Wait for the new base path set-up to propagate through the Source and
210  // Manager to (ReloadConfig() doesn't block on this).
211  do {
212  Env::Default()->SleepForMicroseconds(10 * 1000);
213  available_servables = server_core->ListAvailableServableIds();
214  } while (available_servables.empty() ||
215  available_servables.at(0).version !=
216  test_util::kTestModelLargerVersion);
217 
218  // Revert to the one-version path.
219  TF_ASSERT_OK(server_core->ReloadConfig(one_version_config));
220  // Wait for the new base path set-up to propagate through the Source and
221  // Manager to (ReloadConfig() doesn't block on this).
222  do {
223  Env::Default()->SleepForMicroseconds(10 * 1000);
224  available_servables = server_core->ListAvailableServableIds();
225  } while (available_servables.empty() ||
226  available_servables.at(0).version != test_util::kTestModelVersion);
227 }
228 
229 class RelativePathsServerCoreTest : public ServerCoreTest {
230  protected:
231  // Creates a ModelServerConfig instance where the directory name has
232  // been stripped off the ModelConfig::base_path. Instead that prefix
233  // can be supplied via the Options::model_config_list_root_dir.
234  ModelServerConfig GetTestModelServerConfigWithRelativePath(
235  string* optional_config_list_root_dir = nullptr) {
236  using ::tensorflow::io::Basename;
237  using ::tensorflow::io::Dirname;
238 
239  ModelServerConfig result = GetTestModelServerConfigForFakePlatform();
240  const string model_name = result.model_config_list().config(0).name();
241 
242  ModelConfig& relative =
243  *result.mutable_model_config_list()->mutable_config(0);
244  relative.set_name(strings::StrCat(model_name, "_relative"));
245  const string dirname(Dirname(relative.base_path()));
246  const string basename(Basename(relative.base_path()));
247  CHECK(!dirname.empty());
248  CHECK(!basename.empty());
249  relative.set_base_path(basename);
250 
251  if (optional_config_list_root_dir) {
252  *optional_config_list_root_dir = dirname;
253  }
254 
255  return result;
256  }
257 };
258 
259 TEST_P(RelativePathsServerCoreTest, AbsolutePathSucceeds) {
260  std::unique_ptr<ServerCore> server_core;
261  ModelServerConfig absolute = GetTestModelServerConfigForFakePlatform();
262  TF_ASSERT_OK(CreateServerCore(absolute, &server_core));
263 }
264 
265 TEST_P(RelativePathsServerCoreTest, RelativePathFails) {
266  std::unique_ptr<ServerCore> server_core;
267  ModelServerConfig relative = GetTestModelServerConfigWithRelativePath();
268  EXPECT_EQ(error::INVALID_ARGUMENT,
269  CreateServerCore(relative, &server_core).code());
270 }
271 
272 TEST_P(RelativePathsServerCoreTest,
273  AbsoluteAndRelativePathsWithOptionsSucceed) {
274  std::unique_ptr<ServerCore> server_core;
275  ModelServerConfig absolute_and_relative;
276  ModelServerConfig absolute = GetTestModelServerConfigForFakePlatform();
277  ServerCore::Options options = GetDefaultOptions();
278  {
279  string model_config_list_root_dir;
280  ModelServerConfig relative =
281  GetTestModelServerConfigWithRelativePath(&model_config_list_root_dir);
282  // Add the absolute and relative config to absolute. We'll check that both
283  // paths can be interleaved in the same model config list.
284  CHECK_GT(relative.model_config_list().config_size(), 0);
285  CHECK_GT(absolute.model_config_list().config_size(), 0);
286  *absolute_and_relative.mutable_model_config_list()->add_config() =
287  absolute.model_config_list().config(0);
288  *absolute_and_relative.mutable_model_config_list()->add_config() =
289  relative.model_config_list().config(0);
290  options.model_config_list_root_dir = std::move(model_config_list_root_dir);
291  }
292  TF_ASSERT_OK(CreateServerCore(absolute_and_relative, std::move(options),
293  &server_core));
294 }
295 
296 TEST_P(RelativePathsServerCoreTest, AbsolutePathWithEmptyPathFails) {
297  std::unique_ptr<ServerCore> server_core;
298  ModelServerConfig absolute = GetTestModelServerConfigForFakePlatform();
299  ServerCore::Options options = GetDefaultOptions();
300  options.model_config_list_root_dir = ""; // This should fail IsAbsolutePath.
301  EXPECT_EQ(
302  error::INVALID_ARGUMENT,
303  CreateServerCore(absolute, std::move(options), &server_core).code());
304 }
305 
306 TEST_P(RelativePathsServerCoreTest, RelativePathWithOptionsSucceeds) {
307  std::unique_ptr<ServerCore> server_core;
308  ServerCore::Options options = GetDefaultOptions();
309  string model_config_list_root_dir;
310  ModelServerConfig relative =
311  GetTestModelServerConfigWithRelativePath(&model_config_list_root_dir);
312  options.model_config_list_root_dir = std::move(model_config_list_root_dir);
313  TF_ASSERT_OK(CreateServerCore(relative, std::move(options), &server_core));
314 }
315 
316 TEST_P(RelativePathsServerCoreTest, MixedAbsoluteRelativeFails) {
317  std::unique_ptr<ServerCore> server_core;
318  ModelServerConfig mixed = GetTestModelServerConfigForFakePlatform();
319  const ModelServerConfig relative = GetTestModelServerConfigWithRelativePath();
320  *mixed.mutable_model_config_list()->add_config() =
321  relative.model_config_list().config(0);
322  EXPECT_EQ(error::INVALID_ARGUMENT,
323  CreateServerCore(mixed, &server_core).code());
324 }
325 
326 TEST_P(ServerCoreTest, ErroringModel) {
327  ServerCore::Options options = GetDefaultOptions();
328  test_util::StoragePathErrorInjectingSourceAdapterConfig source_adapter_config;
329  source_adapter_config.set_error_message("injected error");
330  ::google::protobuf::Any source_adapter_config_any;
331  source_adapter_config_any.PackFrom(source_adapter_config);
332  (*(*options.platform_config_map
333  .mutable_platform_configs())[test_util::kFakePlatform]
334  .mutable_source_adapter_config()) = source_adapter_config_any;
335  options.model_server_config = GetTestModelServerConfigForFakePlatform();
336  std::unique_ptr<ServerCore> server_core;
337  Status status = ServerCore::Create(std::move(options), &server_core);
338  EXPECT_FALSE(status.ok());
339  EXPECT_THAT(status.ToString(),
340  ::testing::HasSubstr("1 servable(s) did not become available"));
341 
342  // Validate monitoring states.
343  const auto servable_map =
344  server_core->servable_state_monitor()->GetAllServableStates();
345  auto it_servable = servable_map.find(test_util::kTestModelName);
346  ASSERT_NE(it_servable, servable_map.end());
347  ASSERT_THAT(it_servable->second,
348  UnorderedElementsAre(Pair(test_util::kTestModelVersion, _)));
349  const auto state_and_time =
350  it_servable->second.at(test_util::kTestModelVersion);
351  EXPECT_EQ(state_and_time.state.health.code(), absl::StatusCode::kCancelled);
352  EXPECT_THAT(state_and_time.state.health.ToString(),
353  ::testing::HasSubstr("injected error"));
354  EXPECT_EQ(state_and_time.state.manager_state,
355  ServableState::ManagerState::kEnd);
356 }
357 
358 TEST_P(ServerCoreTest, IllegalReconfigurationToCustomConfig) {
359  // Create a ServerCore with ModelConfigList config.
360  std::unique_ptr<ServerCore> server_core;
361  TF_ASSERT_OK(CreateServerCore(GetTestModelServerConfigForFakePlatform(),
362  &server_core));
363 
364  // Reload with a custom config. This is not allowed since the server was
365  // first configured with TensorFlow model platform.
366  ModelServerConfig config;
367  config.mutable_custom_model_config();
368  EXPECT_THAT(server_core->ReloadConfig(config).ToString(),
369  ::testing::HasSubstr("Cannot transition to requested config"));
370 }
371 
372 TEST_P(ServerCoreTest, IllegalReconfigurationFromCustomConfig) {
373  // Create a ServerCore with custom config.
374  std::unique_ptr<ServerCore> server_core;
375  ModelServerConfig config;
376  config.mutable_custom_model_config();
377  TF_ASSERT_OK(CreateServerCore(config, &server_core));
378 
379  // Reload with a ModelConfigList config. This is not allowed, since the
380  // server was first configured with a custom config.
381  EXPECT_THAT(
382  server_core->ReloadConfig(GetTestModelServerConfigForFakePlatform())
383  .ToString(),
384  ::testing::HasSubstr("Cannot transition to requested config"));
385 }
386 
387 TEST_P(ServerCoreTest, IllegalConfigModelTypeAndPlatformSet) {
388  // Create a ServerCore with both model_type and model_platform set.
389  std::unique_ptr<ServerCore> server_core;
390  ModelServerConfig config = GetTestModelServerConfigForFakePlatform();
391  config.mutable_model_config_list()->mutable_config(0)->set_model_type(
392  ModelType::TENSORFLOW);
393  EXPECT_THAT(CreateServerCore(config, &server_core).ToString(),
394  ::testing::HasSubstr("Illegal setting both"));
395 }
396 
397 TEST_P(ServerCoreTest, DeprecatedModelTypeConfig) {
398  // Create a ServerCore with deprecated config.
399  std::unique_ptr<ServerCore> server_core;
400  ModelServerConfig config = GetTestModelServerConfigForTensorflowPlatform();
401  config.mutable_model_config_list()->mutable_config(0)->set_model_platform("");
402  config.mutable_model_config_list()->mutable_config(0)->set_model_type(
403  ModelType::TENSORFLOW);
404  TF_ASSERT_OK(CreateServerCore(config, &server_core));
405 
406  const std::vector<ServableId> available_servables =
407  server_core->ListAvailableServableIds();
408  ASSERT_EQ(available_servables.size(), 1);
409  const ServableId expected_id = {test_util::kTestModelName,
410  test_util::kTestModelVersion};
411  EXPECT_EQ(available_servables.at(0), expected_id);
412 }
413 
414 TEST_P(ServerCoreTest, DuplicateModelNameInConfig) {
415  std::unique_ptr<ServerCore> server_core;
416  ModelServerConfig config = GetTestModelServerConfigForTensorflowPlatform();
417  *config.mutable_model_config_list()->add_config() =
418  config.model_config_list().config(0);
419  EXPECT_FALSE(CreateServerCore(config, &server_core).ok());
420 }
421 
422 TEST_P(ServerCoreTest, UnknownModelPlatform) {
423  std::unique_ptr<ServerCore> server_core;
424  ModelServerConfig config = GetTestModelServerConfigForTensorflowPlatform();
425  config.mutable_model_config_list()->mutable_config(0)->set_model_platform(
426  "not_a_known_platform");
427  EXPECT_FALSE(CreateServerCore(config, &server_core).ok());
428 }
429 
430 // Creates a model name that incorporates 'platform'. Useful for tests that have
431 // one model for a given platform.
432 //
433 // The model names contain a random element, to vary the model name sort order
434 // independently from the platform name order. This is to get regression
435 // coverage of b/65363800. If called twice for a given platform, always returns
436 // the same model name.
437 string ModelNameForPlatform(const string& platform) {
438  static std::map<string, string>* platform_to_model_map = [] {
439  return new std::map<string, string>();
440  }();
441  auto it = platform_to_model_map->find(platform);
442  if (it != platform_to_model_map->end()) {
443  return it->second;
444  }
445  const string random = strings::StrCat(random::New64());
446  const string model_name =
447  strings::StrCat("model_", random, "_for_", platform);
448  (*platform_to_model_map)[platform] = model_name;
449  return model_name;
450 }
451 
452 // Builds a ModelSpec with a model named 'ModelNameForPlatform(platform)' and
453 // version 0.
454 ModelSpec ModelSpecForPlatform(const string& platform) {
455  ModelSpec spec;
456  spec.set_name(ModelNameForPlatform(platform));
457  spec.mutable_version()->set_value(0);
458  return spec;
459 }
460 
461 // Builds a ModelConfig with a model named 'ModelNameForPlatform(platform)',
462 // base path '<root_path>/<model_name>' and platform 'platform'.
463 ModelConfig ModelConfigForPlatform(const string& root_path,
464  const string& platform) {
465  const string model_name = ModelNameForPlatform(platform);
466  ModelConfig config;
467  config.set_name(model_name);
468  config.set_base_path(io::JoinPath(root_path, model_name));
469  config.set_model_platform(platform);
470  return config;
471 }
472 
473 // Creates a directory for the given version of the model.
474 void CreateModelDir(const ModelConfig& model_config, int version) {
475  TF_CHECK_OK(Env::Default()->CreateDir(model_config.base_path()));
476  const string version_str = strings::StrCat(version);
477  TF_CHECK_OK(Env::Default()->CreateDir(
478  io::JoinPath(model_config.base_path(), version_str)));
479 }
480 
481 // Adds 'platform' to 'platform_config_map' with a fake source adapter that
482 // uses suffix 'suffix_for_<platform>'.
483 void CreateFakePlatform(const string& platform,
484  PlatformConfigMap* platform_config_map) {
485  test_util::FakeLoaderSourceAdapterConfig source_adapter_config;
486  source_adapter_config.set_suffix(strings::StrCat("suffix_for_", platform));
487  ::google::protobuf::Any source_adapter_config_any;
488  source_adapter_config_any.PackFrom(source_adapter_config);
489  (*(*platform_config_map->mutable_platform_configs())[platform]
490  .mutable_source_adapter_config()) = source_adapter_config_any;
491 }
492 
493 // Constructs the servable data that a platform's fake source adapter will emit.
494 string ServableDataForPlatform(const string& root_path, const string& platform,
495  int version) {
496  const string version_str = strings::StrCat(version);
497  return io::JoinPath(root_path, ModelNameForPlatform(platform), version_str,
498  strings::StrCat("suffix_for_", platform));
499 }
500 
501 TEST_P(ServerCoreTest, MultiplePlatforms) {
502  const string root_path =
503  io::JoinPath(testing::TmpDir(),
504  strings::StrCat("MultiplePlatforms_", GetNameForTestCase()));
505  TF_ASSERT_OK(Env::Default()->CreateDir(root_path));
506 
507  // Create a ServerCore with two platforms, and one model for each platform.
508  ServerCore::Options options = GetDefaultOptions();
509  options.platform_config_map.Clear();
510  const std::vector<string> platforms = {"platform_0", "platform_1"};
511  for (const string& platform : platforms) {
512  CreateFakePlatform(platform, &options.platform_config_map);
513  const ModelConfig model_config =
514  ModelConfigForPlatform(root_path, platform);
515  *options.model_server_config.mutable_model_config_list()->add_config() =
516  model_config;
517  CreateModelDir(model_config, 0 /* version */);
518  }
519  std::unique_ptr<ServerCore> server_core;
520  TF_ASSERT_OK(ServerCore::Create(std::move(options), &server_core));
521 
522  // Verify the models got loaded via the platform-specific source adapters.
523  for (const string& platform : platforms) {
524  ServableHandle<string> servable_handle;
525  TF_ASSERT_OK(server_core->GetServableHandle<string>(
526  ModelSpecForPlatform(platform), &servable_handle));
527  const string model_name = ModelNameForPlatform(platform);
528  const auto expected_servable_id = ServableId{model_name, 0};
529  EXPECT_EQ(servable_handle.id(), expected_servable_id);
530  EXPECT_EQ(ServableDataForPlatform(root_path, platform, 0 /* version */),
531  *servable_handle);
532  }
533 }
534 
535 TEST_P(ServerCoreTest, MultiplePlatformsWithConfigChange) {
536  const string root_path = io::JoinPath(
537  testing::TmpDir(), strings::StrCat("MultiplePlatformsWithConfigChange_",
538  GetNameForTestCase()));
539  TF_ASSERT_OK(Env::Default()->CreateDir(root_path));
540 
541  // Create config for three platforms, and one model per platform.
542  ServerCore::Options options = GetDefaultOptions();
543  options.platform_config_map.Clear();
544  const std::vector<string> platforms = {"platform_0", "platform_1",
545  "platform_2"};
546  std::vector<ModelConfig> models;
547  for (const string& platform : platforms) {
548  CreateFakePlatform(platform, &options.platform_config_map);
549  const ModelConfig model_config =
550  ModelConfigForPlatform(root_path, platform);
551  models.push_back(model_config);
552  CreateModelDir(model_config, 0 /* version */);
553  }
554 
555  auto verify_model_loaded = [&root_path](ServerCore* server_core,
556  const string& platform) {
557  ServableHandle<string> servable_handle;
558  TF_ASSERT_OK(server_core->GetServableHandle<string>(
559  ModelSpecForPlatform(platform), &servable_handle));
560  const string model_name = ModelNameForPlatform(platform);
561  const auto expected_servable_id = ServableId{model_name, 0};
562  EXPECT_EQ(servable_handle.id(), expected_servable_id);
563  EXPECT_EQ(ServableDataForPlatform(root_path, platform, 0 /* version */),
564  *servable_handle);
565  };
566  auto verify_model_not_loaded = [](ServerCore* server_core,
567  const string& platform) {
568  ServableHandle<string> servable_handle;
569  EXPECT_FALSE(server_core
570  ->GetServableHandle<string>(ModelSpecForPlatform(platform),
571  &servable_handle)
572  .ok());
573  };
574 
575  // Initially configure the ServerCore to have models 0 and 1.
576  ModelServerConfig* initial_model_config = &options.model_server_config;
577  (*initial_model_config->mutable_model_config_list()->add_config()) =
578  models[0];
579  (*initial_model_config->mutable_model_config_list()->add_config()) =
580  models[1];
581  std::unique_ptr<ServerCore> server_core;
582  TF_ASSERT_OK(ServerCore::Create(std::move(options), &server_core));
583  verify_model_loaded(server_core.get(), platforms[0]);
584  verify_model_loaded(server_core.get(), platforms[1]);
585  verify_model_not_loaded(server_core.get(), platforms[2]);
586 
587  // Reload with models 1 and 2.
588  ModelServerConfig new_model_config;
589  (*new_model_config.mutable_model_config_list()->add_config()) = models[1];
590  (*new_model_config.mutable_model_config_list()->add_config()) = models[2];
591  TF_ASSERT_OK(server_core->ReloadConfig(new_model_config));
592  verify_model_not_loaded(server_core.get(), platforms[0]);
593  verify_model_loaded(server_core.get(), platforms[1]);
594  verify_model_loaded(server_core.get(), platforms[2]);
595 }
596 
597 TEST_P(ServerCoreTest, IllegalToChangeModelPlatform) {
598  const string root_path = io::JoinPath(
599  testing::TmpDir(),
600  strings::StrCat("IllegalToChangeModelPlatform_", GetNameForTestCase()));
601  TF_ASSERT_OK(Env::Default()->CreateDir(root_path));
602 
603  ServerCore::Options options = GetDefaultOptions();
604  options.platform_config_map.Clear();
605  const std::vector<string> platforms = {"platform_0", "platform_1"};
606  for (const string& platform : platforms) {
607  CreateFakePlatform(platform, &options.platform_config_map);
608  }
609 
610  // Configure a model for platform 0.
611  ModelServerConfig initial_config;
612  const ModelConfig model_config =
613  ModelConfigForPlatform(root_path, platforms[0]);
614  *initial_config.mutable_model_config_list()->add_config() = model_config;
615  CreateModelDir(model_config, 0 /* version */);
616 
617  options.model_server_config = initial_config;
618  std::unique_ptr<ServerCore> server_core;
619  TF_ASSERT_OK(ServerCore::Create(std::move(options), &server_core));
620 
621  // Attempt to switch the existing model to platform 1.
622  ModelServerConfig new_config = initial_config;
623  new_config.mutable_model_config_list()->mutable_config(0)->set_model_platform(
624  platforms[1]);
625  const Status reconfigure_status = server_core->ReloadConfig(new_config);
626  EXPECT_FALSE(reconfigure_status.ok());
627  EXPECT_THAT(reconfigure_status.ToString(),
628  ::testing::HasSubstr("Illegal to change a model's platform"));
629 }
630 
631 TEST_P(ServerCoreTest, RequestLoggingOff) {
632  // Create a ServerCore with deprecated config.
633  std::unique_ptr<ServerCore> server_core;
634  const ModelServerConfig config =
635  GetTestModelServerConfigForTensorflowPlatform();
636  TF_ASSERT_OK(CreateServerCore(config, &server_core));
637 
638  TF_ASSERT_OK(
639  server_core->Log(PredictRequest(), PredictResponse(), LogMetadata()));
640  auto logger =
641  server_core->StartLoggingStream<PredictRequest, PredictResponse>(
642  LogMetadata(),
643  []() { return std::make_unique<MockPredictionStreamLogger>(); });
644  EXPECT_EQ(logger, nullptr);
645 }
646 
647 TEST_P(ServerCoreTest, RequestLoggingOn) {
648  std::unordered_map<string, FakeLogCollector*> log_collector_map;
649  ServerCore::Options options = GetDefaultOptions();
650  TF_CHECK_OK(ServerRequestLogger::Create(
651  [&](const LoggingConfig& logging_config,
652  std::shared_ptr<RequestLogger>* const request_logger) {
653  const string& filename_prefix =
654  logging_config.log_collector_config().filename_prefix();
655  log_collector_map[filename_prefix] = new FakeLogCollector();
656  const std::vector<string>& tags = {kSavedModelTagServe};
657  auto mock_request_logger = std::shared_ptr<NiceMock<MockRequestLogger>>(
658  new NiceMock<MockRequestLogger>(
659  logging_config, tags, log_collector_map[filename_prefix]));
660  ON_CALL(*mock_request_logger, CreateLogMessage(_, _, _, _))
661  .WillByDefault(Invoke([&](const google::protobuf::Message& actual_request,
662  const google::protobuf::Message& actual_response,
663  const LogMetadata& actual_log_metadata,
664  std::unique_ptr<google::protobuf::Message>* log) {
665  *log = std::unique_ptr<google::protobuf::Any>(
666  new google::protobuf::Any());
667  return absl::OkStatus();
668  }));
669  *request_logger = std::move(mock_request_logger);
670  return absl::OkStatus();
671  },
672  &options.server_request_logger));
673 
674  // We now setup a model-server-config with a model which switches on request
675  // logging.
676  LogCollectorConfig log_collector_config;
677  log_collector_config.set_type("");
678  log_collector_config.set_filename_prefix(test_util::kTestModelName);
679  LoggingConfig logging_config;
680  *logging_config.mutable_log_collector_config() = log_collector_config;
681  logging_config.mutable_sampling_config()->set_sampling_rate(1.0);
682 
683  ModelServerConfig model_server_config =
684  GetTestModelServerConfigForTensorflowPlatform();
685  *model_server_config.mutable_model_config_list()
686  ->mutable_config(0)
687  ->mutable_logging_config() = logging_config;
688  options.model_server_config = model_server_config;
689 
690  std::unique_ptr<ServerCore> server_core;
691  TF_ASSERT_OK(ServerCore::Create(std::move(options), &server_core));
692 
693  // Logging for unary requests.
694  LogMetadata log_metadata0;
695  auto* const model_spec0 = log_metadata0.mutable_model_spec();
696  model_spec0->set_name(test_util::kTestModelName);
697  TF_ASSERT_OK(
698  server_core->Log(PredictRequest(), PredictResponse(), log_metadata0));
699  ASSERT_EQ(1, log_collector_map.size());
700  EXPECT_EQ(1, log_collector_map[test_util::kTestModelName]->collect_count());
701 
702  // Logging for streaming requests.
703  auto* logger_ptr = new MockPredictionStreamLogger();
704  auto logger =
705  server_core->StartLoggingStream<PredictRequest, PredictResponse>(
706  log_metadata0,
707  [logger_ptr]() { return absl::WrapUnique(logger_ptr); });
708  EXPECT_CALL(*logger_ptr, CreateLogMessage(_, _))
709  .WillOnce(Invoke([](const LogMetadata& log_metadata,
710  std::unique_ptr<google::protobuf::Message>* log) {
711  *log = std::make_unique<google::protobuf::Any>();
712  return absl::OkStatus();
713  }));
714  TF_ASSERT_OK(logger->LogMessage());
715  ASSERT_EQ(1, log_collector_map.size());
716  EXPECT_EQ(2, log_collector_map[test_util::kTestModelName]->collect_count());
717 }
718 
719 TEST_P(ServerCoreTest, ModelSpecMultipleVersionsAvailable) {
720  ModelServerConfig two_version_config =
721  GetTestModelServerConfigForFakePlatform();
722  SwitchToHalfPlusTwoWith2Versions(&two_version_config);
723  ServerCore::Options server_core_options = GetDefaultOptions();
724  server_core_options.allow_version_labels = true;
725  std::unique_ptr<ServerCore> server_core;
726  TF_ASSERT_OK(CreateServerCore(two_version_config,
727  std::move(server_core_options), &server_core));
728 
729  test_util::WaitUntilVersionsAvailable(*server_core->servable_state_monitor(),
730  test_util::kTestModelName,
731  test_util::kAspiredVersions);
732 
733  // Do not specify any version number; we should be given the latest version.
734  {
735  ModelSpec model_spec;
736  model_spec.set_name(test_util::kTestModelName);
737  ServableHandle<string> servable_handle;
738  TF_ASSERT_OK(
739  server_core->GetServableHandle<string>(model_spec, &servable_handle));
740  EXPECT_EQ(
741  (ServableId{test_util::kTestModelName,
742  test_util::kTestModelLargerVersion /* larger version */}),
743  servable_handle.id());
744  }
745 
746  // Use ModelSpec::version to request a specific version number.
747  {
748  ModelSpec model_spec;
749  model_spec.set_name(test_util::kTestModelName);
750  model_spec.mutable_version()->set_value(test_util::kTestModelVersion);
751  ServableHandle<string> servable_handle;
752  TF_ASSERT_OK(
753  server_core->GetServableHandle<string>(model_spec, &servable_handle));
754  EXPECT_EQ(
755  (ServableId{test_util::kTestModelName, test_util::kTestModelVersion}),
756  servable_handle.id());
757  }
758 
759  // Assign labels to the two versions of the model.
760  ASSERT_EQ(1, two_version_config.model_config_list().config().size());
761  test_util::MutateModelConfig(&two_version_config)
762  .SetLabelVersion("A", test_util::kTestModelVersion)
763  .SetLabelVersion("B", test_util::kTestModelLargerVersion);
764  TF_ASSERT_OK(server_core->ReloadConfig(two_version_config));
765 
766  // Use ModelSpec::version_label to request the version labeled "A".
767  {
768  ModelSpec model_spec;
769  model_spec.set_name(test_util::kTestModelName);
770  model_spec.set_version_label("A");
771  ServableHandle<string> servable_handle;
772  TF_ASSERT_OK(
773  server_core->GetServableHandle<string>(model_spec, &servable_handle));
774  EXPECT_EQ(
775  (ServableId{test_util::kTestModelName, test_util::kTestModelVersion}),
776  servable_handle.id());
777  }
778 
779  // Use ModelSpec::version_label to request the version labeled "B".
780  {
781  ModelSpec model_spec;
782  model_spec.set_name(test_util::kTestModelName);
783  model_spec.set_version_label("B");
784  ServableHandle<string> servable_handle;
785  TF_ASSERT_OK(
786  server_core->GetServableHandle<string>(model_spec, &servable_handle));
787  EXPECT_EQ((ServableId{test_util::kTestModelName,
788  test_util::kTestModelLargerVersion}),
789  servable_handle.id());
790  }
791 
792  // Use ModelSpec::version_label to request a nonexistent label.
793  {
794  ModelSpec model_spec;
795  model_spec.set_name(test_util::kTestModelName);
796  model_spec.set_version_label("nonexistent label");
797  ServableHandle<string> servable_handle;
798  Status status =
799  server_core->GetServableHandle<string>(model_spec, &servable_handle);
800  ASSERT_FALSE(status.ok());
801  EXPECT_THAT(status.ToString(),
802  ::testing::HasSubstr("Unrecognized servable version label"));
803  }
804 }
805 
806 TEST_P(ServerCoreTest, AssignLabelToUnavailableVersion) {
807  ModelServerConfig two_version_config =
808  GetTestModelServerConfigForFakePlatform();
809  SwitchToHalfPlusTwoWith2Versions(&two_version_config);
810  ServerCore::Options server_core_options = GetDefaultOptions();
811  server_core_options.allow_version_labels = true;
812  std::unique_ptr<ServerCore> server_core;
813  TF_ASSERT_OK(CreateServerCore(two_version_config,
814  std::move(server_core_options), &server_core));
815 
816  // Wait until both aspired versions of the servable have been loaded.
817  test_util::WaitUntilVersionsAvailable(*server_core->servable_state_monitor(),
818  test_util::kTestModelName,
819  test_util::kAspiredVersions);
820 
821  // Attempt to assign a label to a version that isn't one of the loaded ones.
822  ASSERT_EQ(1, two_version_config.model_config_list().config().size());
823  test_util::MutateModelConfig(&two_version_config)
824  .SetLabelVersion("nice try", test_util::kTestModelBogusVersion);
825  Status status = server_core->ReloadConfig(two_version_config);
826  ASSERT_FALSE(status.ok());
827  EXPECT_THAT(status.ToString(),
828  ::testing::HasSubstr("not currently available for inference"));
829 }
830 
831 TEST_P(ServerCoreTest, AssignLabelToUnavailableVersionAllowed) {
832  ModelServerConfig two_version_config =
833  GetTestModelServerConfigForFakePlatform();
834  SwitchToHalfPlusTwoWith2Versions(&two_version_config);
835  ServerCore::Options server_core_options = GetDefaultOptions();
836  server_core_options.allow_version_labels = true;
837  server_core_options.allow_version_labels_for_unavailable_models = true;
838  std::unique_ptr<ServerCore> server_core;
839  TF_ASSERT_OK(CreateServerCore(two_version_config,
840  std::move(server_core_options), &server_core));
841 
842  test_util::WaitUntilVersionsAvailable(*server_core->servable_state_monitor(),
843  test_util::kTestModelName,
844  test_util::kAspiredVersions);
845 
846  // Attempt to assign a label to a version that isn't one of the loaded ones.
847  ASSERT_EQ(1, two_version_config.model_config_list().config().size());
848  test_util::MutateModelConfig(&two_version_config)
849  .SetLabelVersion("nice try", test_util::kTestModelBogusVersion);
850  Status status = server_core->ReloadConfig(two_version_config);
851  EXPECT_TRUE(status.ok());
852 }
853 
854 TEST_P(ServerCoreTest, AssignExistingLabelToUnavailableVersionDisallowed) {
855  ModelServerConfig two_version_config =
856  GetTestModelServerConfigForFakePlatform();
857  SwitchToHalfPlusTwoWith2Versions(&two_version_config);
858  ServerCore::Options server_core_options = GetDefaultOptions();
859  server_core_options.allow_version_labels = true;
860  server_core_options.allow_version_labels_for_unavailable_models = true;
861  std::unique_ptr<ServerCore> server_core;
862  TF_ASSERT_OK(CreateServerCore(two_version_config,
863  std::move(server_core_options), &server_core));
864 
865  test_util::WaitUntilVersionsAvailable(*server_core->servable_state_monitor(),
866  test_util::kTestModelName,
867  test_util::kAspiredVersions);
868 
869  ASSERT_EQ(1, two_version_config.model_config_list().config().size());
870  test_util::MutateModelConfig(&two_version_config)
871  .SetLabelVersion("A", test_util::kTestModelVersion)
872  .SetLabelVersion("B", test_util::kTestModelLargerVersion);
873  TF_ASSERT_OK(server_core->ReloadConfig(two_version_config));
874 
875  // Attempt to assign an existing label to a different version that isn't
876  // one of the loaded ones.
877  ASSERT_EQ(1, two_version_config.model_config_list().config().size());
878  test_util::MutateModelConfig(&two_version_config)
879  .SetLabelVersion("A", test_util::kTestModelBogusVersion);
880  Status status = server_core->ReloadConfig(two_version_config);
881  ASSERT_FALSE(status.ok());
882  EXPECT_THAT(status.ToString(),
883  ::testing::HasSubstr("not currently available for inference"));
884 }
885 
886 TEST_P(ServerCoreTest, AssignExistingLabelToUnavailableVersionAllowed) {
887  ModelServerConfig two_version_config =
888  GetTestModelServerConfigForFakePlatform();
889  SwitchToHalfPlusTwoWith2Versions(&two_version_config);
890  ServerCore::Options server_core_options = GetDefaultOptions();
891  server_core_options.allow_version_labels = true;
892  server_core_options.force_allow_any_version_labels_for_unavailable_models =
893  true;
894  std::unique_ptr<ServerCore> server_core;
895  TF_ASSERT_OK(CreateServerCore(two_version_config,
896  std::move(server_core_options), &server_core));
897 
898  test_util::WaitUntilVersionsAvailable(*server_core->servable_state_monitor(),
899  test_util::kTestModelName,
900  test_util::kAspiredVersions);
901 
902  ASSERT_EQ(1, two_version_config.model_config_list().config().size());
903  test_util::MutateModelConfig(&two_version_config)
904  .SetLabelVersion("A", test_util::kTestModelVersion)
905  .SetLabelVersion("B", test_util::kTestModelLargerVersion);
906  TF_ASSERT_OK(server_core->ReloadConfig(two_version_config));
907 
908  // Attempt to assign an existing label to a different version that isn't
909  // one of the loaded ones.
910  ASSERT_EQ(1, two_version_config.model_config_list().config().size());
911  test_util::MutateModelConfig(&two_version_config)
912  .SetLabelVersion("A", test_util::kTestModelBogusVersion);
913  TF_ASSERT_OK(server_core->ReloadConfig(two_version_config));
914 }
915 
916 TEST_P(ServerCoreTest, VersionLabelsNotAllowed) {
917  ServerCore::Options server_core_options = GetDefaultOptions();
918  server_core_options.allow_version_labels = false;
919  ModelServerConfig config = GetTestModelServerConfigForFakePlatform();
920  std::unique_ptr<ServerCore> server_core;
921  TF_ASSERT_OK(
922  CreateServerCore(config, std::move(server_core_options), &server_core));
923 
924  test_util::WaitUntilVersionsAvailable(*server_core->servable_state_monitor(),
925  test_util::kTestModelName,
926  {test_util::kTestModelVersion});
927 
928  ASSERT_EQ(1, config.model_config_list().config().size());
929  test_util::MutateModelConfig(&config).SetLabelVersion(
930  "A", test_util::kTestModelVersion);
931  Status status = server_core->ReloadConfig(config);
932  ASSERT_FALSE(status.ok());
933  EXPECT_THAT(
934  status.ToString(),
935  ::testing::HasSubstr("Model version labels are not currently allowed"));
936 }
937 
938 TEST_P(ServerCoreTest, StoragePathPrefixOption) {
939  if (PrefixPathsWithURIScheme()) {
940  return;
941  }
942  if (GetTestType() != SAVED_MODEL) {
943  return;
944  }
945 
946  // Remove the path prefix from model base path in the ModelServerConfig, and
947  // add the prefix to ServerCore::Option::storage_path_prefix.
948  ModelServerConfig config = GetTestModelServerConfigForFakePlatform();
949  string base_path = config.model_config_list().config(0).base_path();
950  const string path_prefix = tensorflow::io::JoinPath(
951  getenv("TEST_SRCDIR"), "tf_serving/external/org_tensorflow/tensorflow/");
952  config.mutable_model_config_list()->mutable_config(0)->set_base_path(
953  string(absl::StripPrefix(base_path, path_prefix)));
954  ServerCore::Options options = GetDefaultOptions();
955  options.storage_path_prefix = path_prefix;
956  std::unique_ptr<ServerCore> server_core;
957  TF_ASSERT_OK(CreateServerCore(GetTestModelServerConfigForFakePlatform(),
958  std::move(options), &server_core));
959 
960  const std::vector<ServableId> available_servables =
961  server_core->ListAvailableServableIds();
962  ASSERT_EQ(available_servables.size(), 1);
963  const ServableId expected_id = {test_util::kTestModelName,
964  test_util::kTestModelVersion};
965  EXPECT_EQ(available_servables.at(0), expected_id);
966 
967  ModelSpec model_spec;
968  model_spec.set_name(test_util::kTestModelName);
969  model_spec.mutable_version()->set_value(test_util::kTestModelVersion);
970  ServableHandle<string> servable_handle;
971  TF_ASSERT_OK(
972  server_core->GetServableHandle<string>(model_spec, &servable_handle));
973  EXPECT_EQ(servable_handle.id(), expected_id);
974 }
975 
976 INSTANTIATE_TEST_CASE_P(
977  TestType, ServerCoreTest,
978  ::testing::Combine(
979  IsTensorflowServingOSS()
980  ? ::testing::Range(
981  static_cast<int>(test_util::ServerCoreTest::SAVED_MODEL),
982  static_cast<int>(test_util::ServerCoreTest::NUM_TEST_TYPES))
983  : ::testing::Range(
984  0, static_cast<int>(ServerCoreTest::NUM_TEST_TYPES)),
985  ::testing::Bool()));
986 
987 INSTANTIATE_TEST_CASE_P(
988  TestType, RelativePathsServerCoreTest,
989  ::testing::Combine(
990  IsTensorflowServingOSS()
991  ? ::testing::Range(
992  static_cast<int>(test_util::ServerCoreTest::SAVED_MODEL),
993  static_cast<int>(test_util::ServerCoreTest::NUM_TEST_TYPES))
994  : ::testing::Range(
995  0, static_cast<int>(ServerCoreTest::NUM_TEST_TYPES)),
996  ::testing::Bool()));
997 
998 } // namespace
999 } // namespace serving
1000 } // namespace tensorflow
static Status Create(Options options, std::unique_ptr< ServerCore > *core)
Definition: server_core.cc:231