test_libhdfs_mini_stress.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include "common/util_c.h"
  19. #include "expect.h"
  20. #include "hdfs/hdfs.h"
  21. #include "hdfspp/hdfs_ext.h"
  22. #include "native_mini_dfs.h"
  23. #include "os/thread.h"
  24. #include "x-platform/c-api/syscall.h"
  25. #include <errno.h>
  26. #include <inttypes.h>
  27. #include <pwd.h>
  28. #include <stdint.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <sys/socket.h>
  33. #include <sys/types.h>
  34. #include <sys/wait.h>
  35. #include <unistd.h>
  36. #define TO_STR_HELPER(X) #X
  37. #define TO_STR(X) TO_STR_HELPER(X)
  38. #define TLH_MAX_THREADS 10000
  39. #define TLH_MAX_DNS 16
  40. #define TLH_DEFAULT_BLOCK_SIZE 1048576
  41. #define TLH_DEFAULT_DFS_REPLICATION 3
  42. #define TLH_DEFAULT_IPC_CLIENT_CONNECT_MAX_RETRIES 100
  43. #define TLH_DEFAULT_IPC_CLIENT_CONNECT_RETRY_INTERVAL_MS 5
  44. #define MAX_DIGITS_IN_INT 10
  45. #ifndef RANDOM_ERROR_RATIO
  46. #define RANDOM_ERROR_RATIO 1000000000
  47. #endif
  48. struct tlhThreadInfo {
  49. /** Thread index */
  50. int threadIdx;
  51. /** 0 = thread was successful; error code otherwise */
  52. int success;
  53. /** thread identifier */
  54. thread theThread;
  55. /** fs, shared with other threads **/
  56. hdfsFS hdfs;
  57. /** Filename */
  58. const char *fileNm;
  59. };
  60. static int hdfsNameNodeConnect(tPort port, hdfsFS *fs,
  61. const char *username)
  62. {
  63. int ret;
  64. hdfsFS hdfs;
  65. struct hdfsBuilder *bld;
  66. if (port < 0) {
  67. fprintf(stderr, "hdfsNameNodeConnect: nmdGetNameNodePort "
  68. "returned error %d\n", port);
  69. return port;
  70. }
  71. bld = hdfsNewBuilder();
  72. if (!bld)
  73. return -ENOMEM;
  74. hdfsBuilderSetForceNewInstance(bld);
  75. hdfsBuilderSetNameNode(bld, "localhost");
  76. hdfsBuilderSetNameNodePort(bld, port);
  77. hdfsBuilderConfSetStr(bld, "dfs.block.size",
  78. TO_STR(TLH_DEFAULT_BLOCK_SIZE));
  79. hdfsBuilderConfSetStr(bld, "dfs.blocksize",
  80. TO_STR(TLH_DEFAULT_BLOCK_SIZE));
  81. hdfsBuilderConfSetStr(bld, "dfs.replication",
  82. TO_STR(TLH_DEFAULT_DFS_REPLICATION));
  83. hdfsBuilderConfSetStr(bld, "ipc.client.connect.max.retries",
  84. TO_STR(TLH_DEFAULT_IPC_CLIENT_CONNECT_MAX_RETRIES));
  85. hdfsBuilderConfSetStr(bld, "ipc.client.connect.retry.interval",
  86. TO_STR(TLH_DEFAULT_IPC_CLIENT_CONNECT_RETRY_INTERVAL_MS));
  87. if (username) {
  88. hdfsBuilderSetUserName(bld, username);
  89. }
  90. hdfs = hdfsBuilderConnect(bld);
  91. if (!hdfs) {
  92. ret = -errno;
  93. return ret;
  94. }
  95. *fs = hdfs;
  96. return 0;
  97. }
  98. #ifdef VALGRIND
  99. static int hdfsCurlData(const char *host, const tPort port, const char *dirNm,
  100. const char *fileNm, tSize fileSz)
  101. {
  102. int ret;
  103. const char *content;
  104. content = fileNm;
  105. char tmpFile[14] = "stress_XXXXXX";
  106. // Retrieve user id (always successful)
  107. uid_t uid = geteuid();
  108. // Retrieve password struct entry
  109. struct passwd *pw;
  110. EXPECT_NONNULL(pw = getpwuid(uid));
  111. int fd = -1;
  112. EXPECT_NONNEGATIVE(fd = x_platform_syscall_create_and_open_temp_file(
  113. tmpFile, sizeof tmpFile));
  114. tSize sz = 0;
  115. while (sz < fileSz) {
  116. EXPECT_NONNEGATIVE(ret = write(fd, content, strlen(content)));
  117. sz += ret;
  118. }
  119. int curlSize = 200;
  120. char curlStr[curlSize];
  121. ret = snprintf(curlStr,curlSize,"curl -L -i -X PUT -T %s \"http://%s:%d/webhdfs/v1%s?op=CREATE&overwrite=true&user.name=%s\"",tmpFile,host,(int)port,fileNm,pw->pw_name);
  122. //Check for errors during snprintf
  123. EXPECT_NONNEGATIVE(ret);
  124. //Check for truncation during snprintf
  125. EXPECT_INT_LT(ret, curlSize);
  126. EXPECT_ZERO(system(curlStr));
  127. EXPECT_ZERO(unlink(tmpFile));
  128. return 0;
  129. }
  130. #else
  131. static int hdfsWriteData(hdfsFS hdfs, const char *dirNm,
  132. const char *fileNm, tSize fileSz)
  133. {
  134. hdfsFile file;
  135. int ret, expected;
  136. const char *content;
  137. content = fileNm;
  138. if (hdfsExists(hdfs, dirNm) == 0) {
  139. EXPECT_ZERO(hdfsDelete(hdfs, dirNm, 1));
  140. }
  141. EXPECT_ZERO(hdfsCreateDirectory(hdfs, dirNm));
  142. file = hdfsOpenFile(hdfs, fileNm, O_WRONLY, 0, 0, 0);
  143. EXPECT_NONNULL(file);
  144. expected = (int)strlen(content);
  145. tSize sz = 0;
  146. while (sz < fileSz) {
  147. ret = hdfsWrite(hdfs, file, content, expected);
  148. if (ret < 0) {
  149. ret = errno;
  150. fprintf(stderr, "hdfsWrite failed and set errno %d\n", ret);
  151. return ret;
  152. }
  153. if (ret != expected) {
  154. fprintf(stderr, "hdfsWrite was supposed to write %d bytes, but "
  155. "it wrote %d\n", ret, expected);
  156. return EIO;
  157. }
  158. sz += ret;
  159. }
  160. EXPECT_ZERO(hdfsFlush(hdfs, file));
  161. EXPECT_ZERO(hdfsHSync(hdfs, file));
  162. EXPECT_ZERO(hdfsCloseFile(hdfs, file));
  163. return 0;
  164. }
  165. #endif
  166. static int fileEventCallback1(const char * event, const char * cluster, const char * file, int64_t value, int64_t cookie)
  167. {
  168. char * randomErrRatioStr = getenv("RANDOM_ERROR_RATIO");
  169. int64_t randomErrRatio = RANDOM_ERROR_RATIO;
  170. if (randomErrRatioStr) randomErrRatio = (int64_t)atoi(randomErrRatioStr);
  171. if (randomErrRatio == 0) return DEBUG_SIMULATE_ERROR;
  172. else if (randomErrRatio < 0) return LIBHDFSPP_EVENT_OK;
  173. return random() % randomErrRatio == 0 ? DEBUG_SIMULATE_ERROR : LIBHDFSPP_EVENT_OK;
  174. }
  175. static int fileEventCallback2(const char * event, const char * cluster, const char * file, int64_t value, int64_t cookie)
  176. {
  177. /* no op */
  178. return LIBHDFSPP_EVENT_OK;
  179. }
  180. static int doTestHdfsMiniStress(struct tlhThreadInfo *ti, int randomErr)
  181. {
  182. char tmp[4096];
  183. hdfsFile file;
  184. int ret, expected;
  185. hdfsFileInfo *fileInfo;
  186. uint64_t readOps, nErrs=0;
  187. tOffset seekPos;
  188. const char *content;
  189. content = ti->fileNm;
  190. expected = (int)strlen(content);
  191. fileInfo = hdfsGetPathInfo(ti->hdfs, ti->fileNm);
  192. EXPECT_NONNULL(fileInfo);
  193. file = hdfsOpenFile(ti->hdfs, ti->fileNm, O_RDONLY, 0, 0, 0);
  194. EXPECT_NONNULL(file);
  195. libhdfspp_file_event_callback callback = (randomErr != 0) ? &fileEventCallback1 : &fileEventCallback2;
  196. hdfsPreAttachFileMonitor(callback, 0);
  197. fprintf(stderr, "testHdfsMiniStress(threadIdx=%d): starting read loop\n",
  198. ti->threadIdx);
  199. for (readOps=0; readOps < 1000; ++readOps) {
  200. EXPECT_ZERO(hdfsCloseFile(ti->hdfs, file));
  201. file = hdfsOpenFile(ti->hdfs, ti->fileNm, O_RDONLY, 0, 0, 0);
  202. EXPECT_NONNULL(file);
  203. seekPos = (((double)random()) / RAND_MAX) * (fileInfo->mSize - expected);
  204. seekPos = (seekPos / expected) * expected;
  205. ret = hdfsSeek(ti->hdfs, file, seekPos);
  206. if (ret < 0) {
  207. ret = errno;
  208. fprintf(stderr, "hdfsSeek to %"PRIu64" failed and set"
  209. " errno %d\n", seekPos, ret);
  210. ++nErrs;
  211. continue;
  212. }
  213. ret = hdfsRead(ti->hdfs, file, tmp, expected);
  214. if (ret < 0) {
  215. ret = errno;
  216. fprintf(stderr, "hdfsRead failed and set errno %d\n", ret);
  217. ++nErrs;
  218. continue;
  219. }
  220. if (ret != expected) {
  221. fprintf(stderr, "hdfsRead was supposed to read %d bytes, but "
  222. "it read %d\n", ret, expected);
  223. ++nErrs;
  224. continue;
  225. }
  226. ret = memcmp(content, tmp, expected);
  227. if (ret) {
  228. fprintf(stderr, "hdfsRead result (%.*s) does not match expected (%.*s)",
  229. expected, tmp, expected, content);
  230. ++nErrs;
  231. continue;
  232. }
  233. }
  234. EXPECT_ZERO(hdfsCloseFile(ti->hdfs, file));
  235. fprintf(stderr, "testHdfsMiniStress(threadIdx=%d): finished read loop\n",
  236. ti->threadIdx);
  237. EXPECT_ZERO(nErrs);
  238. hdfsFreeFileInfo(fileInfo, 1);
  239. return 0;
  240. }
  241. static int testHdfsMiniStressImpl(struct tlhThreadInfo *ti)
  242. {
  243. fprintf(stderr, "testHdfsMiniStress(threadIdx=%d): starting\n",
  244. ti->threadIdx);
  245. EXPECT_NONNULL(ti->hdfs);
  246. // Error injection on, some failures are expected in the read path.
  247. // The expectation is that any memory stomps will cascade and cause
  248. // the following test to fail. Ideally RPC errors would be separated
  249. // from BlockReader errors (RPC is expected to recover from disconnects).
  250. doTestHdfsMiniStress(ti, 1);
  251. // No error injection
  252. EXPECT_ZERO(doTestHdfsMiniStress(ti, 0));
  253. return 0;
  254. }
  255. static void testHdfsMiniStress(void *v)
  256. {
  257. struct tlhThreadInfo *ti = (struct tlhThreadInfo*)v;
  258. int ret = testHdfsMiniStressImpl(ti);
  259. ti->success = ret;
  260. }
  261. static int checkFailures(struct tlhThreadInfo *ti, int tlhNumThreads)
  262. {
  263. int i, threadsFailed = 0;
  264. const char *sep = "";
  265. for (i = 0; i < tlhNumThreads; i++) {
  266. if (ti[i].success != 0) {
  267. threadsFailed = 1;
  268. }
  269. }
  270. if (!threadsFailed) {
  271. fprintf(stderr, "testLibHdfsMiniStress: all threads succeeded. SUCCESS.\n");
  272. return EXIT_SUCCESS;
  273. }
  274. fprintf(stderr, "testLibHdfsMiniStress: some threads failed: [");
  275. for (i = 0; i < tlhNumThreads; i++) {
  276. if (ti[i].success != 0) {
  277. fprintf(stderr, "%s%d", sep, i);
  278. sep = ", ";
  279. }
  280. }
  281. fprintf(stderr, "]. FAILURE.\n");
  282. return EXIT_FAILURE;
  283. }
  284. /**
  285. * Test intended to stress libhdfs client with concurrent requests. Currently focused
  286. * on concurrent reads. In order to run this test under valgrind and avoid JVM issues
  287. * we fork a child process that runs a mini dfs cluster, and the parent process
  288. * communicates with it using a socket pair.
  289. */
  290. int main(int argc, char *argv[])
  291. {
  292. tPort port;
  293. #ifdef VALGRIND
  294. int httpPort;
  295. char * httpHost;
  296. size_t hostSize;
  297. int fds[2];
  298. static const int parentsocket = 0;
  299. static const int childsocket = 1;
  300. int status;
  301. // If there is an argument, the child code executes and starts a mini dfs cluster
  302. if (argc > 1) {
  303. // The argument contains child socket
  304. fds[childsocket] = (int) strtol(argv[1],NULL,10);
  305. #endif
  306. const char *tlhNumDNsStr;
  307. struct NativeMiniDfsCluster* tlhCluster;
  308. struct NativeMiniDfsConf conf = {
  309. 1, /* doFormat */
  310. 1, /* webhdfs */
  311. 0, /* webhdfs port */
  312. 1 /* shortcircuit */
  313. };
  314. tlhNumDNsStr = getenv("TLH_NUM_DNS");
  315. if (!tlhNumDNsStr) {
  316. tlhNumDNsStr = "1";
  317. }
  318. conf.numDataNodes = atoi(tlhNumDNsStr);
  319. if ((conf.numDataNodes <= 0) || (conf.numDataNodes > TLH_MAX_DNS)) {
  320. fprintf(stderr, "testLibHdfsMiniStress: must have a number of datanodes "
  321. "between 1 and %d inclusive, not %d\n",
  322. TLH_MAX_DNS, conf.numDataNodes);
  323. return EXIT_FAILURE;
  324. }
  325. tlhCluster = nmdCreate(&conf);
  326. EXPECT_NONNULL(tlhCluster);
  327. EXPECT_ZERO(nmdWaitClusterUp(tlhCluster));
  328. port = (tPort)nmdGetNameNodePort(tlhCluster);
  329. #ifdef VALGRIND
  330. EXPECT_ZERO(nmdGetNameNodeHttpAddress(tlhCluster, &httpPort, (const char **) &httpHost));
  331. hostSize = strlen(httpHost) + 1;
  332. // The child is sending hdfs port, webhdfs port, hostname size, and hostname to the parent
  333. ASSERT_INT64_EQ(write(fds[childsocket], &port, sizeof(tPort)), sizeof(tPort));
  334. ASSERT_INT64_EQ(write(fds[childsocket], &httpPort, sizeof(int)), sizeof(int));
  335. ASSERT_INT64_EQ(write(fds[childsocket], &hostSize, sizeof(size_t)), sizeof(size_t));
  336. ASSERT_INT64_EQ(write(fds[childsocket], httpHost, hostSize), hostSize);
  337. free(httpHost);
  338. // The child is waiting for the parent to finish and send a message
  339. ASSERT_INT64_EQ(read(fds[childsocket], &port, sizeof(tPort)), sizeof(tPort));
  340. EXPECT_ZERO(nmdShutdown(tlhCluster));
  341. nmdFree(tlhCluster);
  342. } else { // If there is no argument, the parent code executes
  343. #endif
  344. hdfsFS hdfs = NULL;
  345. int i, tlhNumThreads;
  346. char *dirNm, *fileNm;
  347. tSize fileSz;
  348. const char *tlhNumThreadsStr;
  349. struct tlhThreadInfo ti[TLH_MAX_THREADS];
  350. dirNm = "/tlhMiniStressData";
  351. fileNm = "/tlhMiniStressData/file";
  352. fileSz = 2*1024*1024;
  353. tlhNumThreadsStr = getenv("TLH_NUM_THREADS");
  354. if (!tlhNumThreadsStr) {
  355. tlhNumThreadsStr = "8";
  356. }
  357. tlhNumThreads = atoi(tlhNumThreadsStr);
  358. if ((tlhNumThreads <= 0) || (tlhNumThreads > TLH_MAX_THREADS)) {
  359. fprintf(stderr, "testLibHdfsMiniStress: must have a number of threads "
  360. "between 1 and %d inclusive, not %d\n",
  361. TLH_MAX_THREADS, tlhNumThreads);
  362. return EXIT_FAILURE;
  363. }
  364. memset(&ti[0], 0, sizeof(ti));
  365. for (i = 0; i < tlhNumThreads; i++) {
  366. ti[i].threadIdx = i;
  367. }
  368. #ifdef VALGRIND
  369. EXPECT_ZERO(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds));
  370. // Forking off a child to execute JVM stuff
  371. pid_t pid = fork();
  372. if(pid == 0){
  373. // The child execs this program from the beginning and passes
  374. // its file descriptor as a command line argument.
  375. char ch_fd[MAX_DIGITS_IN_INT + 1];
  376. sprintf(ch_fd, "%d", fds[childsocket]);
  377. // This has to be done with exec() to prevent valgrind from tracing the child
  378. execl(argv[0], argv[0], ch_fd, NULL);
  379. // This code should never execute
  380. fprintf(stderr, "execl() failed.\n");
  381. return EXIT_FAILURE;
  382. }
  383. close(fds[childsocket]);
  384. // The parent is receiving hdfs port, webhdfs port, hostname size, and hostname from the child
  385. ASSERT_INT64_EQ(read(fds[parentsocket], &port, sizeof(tPort)), sizeof(tPort));
  386. ASSERT_INT64_EQ(read(fds[parentsocket], &httpPort, sizeof(int)), sizeof(int));
  387. ASSERT_INT64_EQ(read(fds[parentsocket], &hostSize, sizeof(size_t)), sizeof(size_t));
  388. httpHost = malloc(hostSize);
  389. ASSERT_INT64_EQ(read(fds[parentsocket], httpHost, hostSize), hostSize);
  390. EXPECT_ZERO(hdfsNameNodeConnect(port, &hdfs, NULL));
  391. EXPECT_ZERO(hdfsCurlData(httpHost, httpPort, dirNm, fileNm, fileSz));
  392. free(httpHost);
  393. #else
  394. EXPECT_ZERO(hdfsNameNodeConnect(port, &hdfs, NULL));
  395. EXPECT_ZERO(hdfsWriteData(hdfs, dirNm, fileNm, fileSz));
  396. #endif
  397. // Multi-threaded reads.
  398. for (i = 0; i < tlhNumThreads; i++) {
  399. ti[i].theThread.start = testHdfsMiniStress;
  400. ti[i].theThread.arg = &ti[i];
  401. ti[i].hdfs = hdfs;
  402. ti[i].fileNm = fileNm;
  403. EXPECT_ZERO(threadCreate(&ti[i].theThread));
  404. }
  405. for (i = 0; i < tlhNumThreads; i++) {
  406. EXPECT_ZERO(threadJoin(&ti[i].theThread));
  407. }
  408. EXPECT_ZERO(hdfsDisconnect(hdfs));
  409. EXPECT_ZERO(checkFailures(ti, tlhNumThreads));
  410. #ifdef VALGRIND
  411. //Send this message to the child to notify it that it can now shut down
  412. ASSERT_INT64_EQ(write(fds[parentsocket], &port, sizeof(tPort)), sizeof(tPort));
  413. // Wait for the child to exit and verify it returned EXIT_SUCCESS
  414. waitpid(pid, &status, 0);
  415. EXPECT_ZERO(status);
  416. }
  417. #else
  418. EXPECT_ZERO(nmdShutdown(tlhCluster));
  419. nmdFree(tlhCluster);
  420. #endif
  421. // Clean up static data and prevent valgrind memory leaks
  422. ShutdownProtobufLibrary_C();
  423. return EXIT_SUCCESS;
  424. }