test_libhdfs_mini_stress.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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 "expect.h"
  19. #include "hdfs/hdfs.h"
  20. #include "hdfspp/hdfs_ext.h"
  21. #include "native_mini_dfs.h"
  22. #include "os/thread.h"
  23. #include <errno.h>
  24. #include <inttypes.h>
  25. #include <stdint.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #define TO_STR_HELPER(X) #X
  30. #define TO_STR(X) TO_STR_HELPER(X)
  31. #define TLH_MAX_THREADS 10000
  32. #define TLH_MAX_DNS 16
  33. #define TLH_DEFAULT_BLOCK_SIZE 1048576
  34. #define TLH_DEFAULT_DFS_REPLICATION 3
  35. #define TLH_DEFAULT_IPC_CLIENT_CONNECT_MAX_RETRIES 100
  36. #define TLH_DEFAULT_IPC_CLIENT_CONNECT_RETRY_INTERVAL_MS 5
  37. #ifndef RANDOM_ERROR_RATIO
  38. #define RANDOM_ERROR_RATIO 1000000000
  39. #endif
  40. struct tlhThreadInfo {
  41. /** Thread index */
  42. int threadIdx;
  43. /** 0 = thread was successful; error code otherwise */
  44. int success;
  45. /** thread identifier */
  46. thread theThread;
  47. /** fs, shared with other threads **/
  48. hdfsFS hdfs;
  49. /** Filename */
  50. const char *fileNm;
  51. };
  52. static int hdfsNameNodeConnect(struct NativeMiniDfsCluster *cl, hdfsFS *fs,
  53. const char *username)
  54. {
  55. int ret;
  56. tPort port;
  57. hdfsFS hdfs;
  58. struct hdfsBuilder *bld;
  59. port = (tPort)nmdGetNameNodePort(cl);
  60. if (port < 0) {
  61. fprintf(stderr, "hdfsNameNodeConnect: nmdGetNameNodePort "
  62. "returned error %d\n", port);
  63. return port;
  64. }
  65. bld = hdfsNewBuilder();
  66. if (!bld)
  67. return -ENOMEM;
  68. hdfsBuilderSetForceNewInstance(bld);
  69. hdfsBuilderSetNameNode(bld, "localhost");
  70. hdfsBuilderSetNameNodePort(bld, port);
  71. hdfsBuilderConfSetStr(bld, "dfs.block.size",
  72. TO_STR(TLH_DEFAULT_BLOCK_SIZE));
  73. hdfsBuilderConfSetStr(bld, "dfs.blocksize",
  74. TO_STR(TLH_DEFAULT_BLOCK_SIZE));
  75. hdfsBuilderConfSetStr(bld, "dfs.replication",
  76. TO_STR(TLH_DEFAULT_DFS_REPLICATION));
  77. hdfsBuilderConfSetStr(bld, "ipc.client.connect.max.retries",
  78. TO_STR(TLH_DEFAULT_IPC_CLIENT_CONNECT_MAX_RETRIES));
  79. hdfsBuilderConfSetStr(bld, "ipc.client.connect.retry.interval",
  80. TO_STR(TLH_DEFAULT_IPC_CLIENT_CONNECT_RETRY_INTERVAL_MS));
  81. if (username) {
  82. hdfsBuilderSetUserName(bld, username);
  83. }
  84. hdfs = hdfsBuilderConnect(bld);
  85. if (!hdfs) {
  86. ret = -errno;
  87. return ret;
  88. }
  89. *fs = hdfs;
  90. return 0;
  91. }
  92. static int hdfsWriteData(hdfsFS hdfs, const char *dirNm,
  93. const char *fileNm, tSize fileSz)
  94. {
  95. hdfsFile file;
  96. int ret, expected;
  97. const char *content;
  98. content = fileNm;
  99. if (hdfsExists(hdfs, dirNm) == 0) {
  100. EXPECT_ZERO(hdfsDelete(hdfs, dirNm, 1));
  101. }
  102. EXPECT_ZERO(hdfsCreateDirectory(hdfs, dirNm));
  103. file = hdfsOpenFile(hdfs, fileNm, O_WRONLY, 0, 0, 0);
  104. EXPECT_NONNULL(file);
  105. expected = (int)strlen(content);
  106. tSize sz = 0;
  107. while (sz < fileSz) {
  108. ret = hdfsWrite(hdfs, file, content, expected);
  109. if (ret < 0) {
  110. ret = errno;
  111. fprintf(stderr, "hdfsWrite failed and set errno %d\n", ret);
  112. return ret;
  113. }
  114. if (ret != expected) {
  115. fprintf(stderr, "hdfsWrite was supposed to write %d bytes, but "
  116. "it wrote %d\n", ret, expected);
  117. return EIO;
  118. }
  119. sz += ret;
  120. }
  121. EXPECT_ZERO(hdfsFlush(hdfs, file));
  122. EXPECT_ZERO(hdfsHSync(hdfs, file));
  123. EXPECT_ZERO(hdfsCloseFile(hdfs, file));
  124. return 0;
  125. }
  126. static int fileEventCallback1(const char * event, const char * cluster, const char * file, int64_t value, int64_t cookie)
  127. {
  128. char * randomErrRatioStr = getenv("RANDOM_ERROR_RATIO");
  129. int64_t randomErrRatio = RANDOM_ERROR_RATIO;
  130. if (randomErrRatioStr) randomErrRatio = (int64_t)atoi(randomErrRatioStr);
  131. if (randomErrRatio == 0) return DEBUG_SIMULATE_ERROR;
  132. else if (randomErrRatio < 0) return LIBHDFSPP_EVENT_OK;
  133. return random() % randomErrRatio == 0 ? DEBUG_SIMULATE_ERROR : LIBHDFSPP_EVENT_OK;
  134. }
  135. static int fileEventCallback2(const char * event, const char * cluster, const char * file, int64_t value, int64_t cookie)
  136. {
  137. /* no op */
  138. return LIBHDFSPP_EVENT_OK;
  139. }
  140. static int doTestHdfsMiniStress(struct tlhThreadInfo *ti, int randomErr)
  141. {
  142. char tmp[4096];
  143. hdfsFile file;
  144. int ret, expected;
  145. hdfsFileInfo *fileInfo;
  146. uint64_t readOps, nErrs=0;
  147. tOffset seekPos;
  148. const char *content;
  149. content = ti->fileNm;
  150. expected = (int)strlen(content);
  151. fileInfo = hdfsGetPathInfo(ti->hdfs, ti->fileNm);
  152. EXPECT_NONNULL(fileInfo);
  153. file = hdfsOpenFile(ti->hdfs, ti->fileNm, O_RDONLY, 0, 0, 0);
  154. EXPECT_NONNULL(file);
  155. libhdfspp_file_event_callback callback = (randomErr != 0) ? &fileEventCallback1 : &fileEventCallback2;
  156. hdfsPreAttachFileMonitor(callback, 0);
  157. fprintf(stderr, "testHdfsMiniStress(threadIdx=%d): starting read loop\n",
  158. ti->threadIdx);
  159. for (readOps=0; readOps < 1000; ++readOps) {
  160. EXPECT_ZERO(hdfsCloseFile(ti->hdfs, file));
  161. file = hdfsOpenFile(ti->hdfs, ti->fileNm, O_RDONLY, 0, 0, 0);
  162. EXPECT_NONNULL(file);
  163. seekPos = (((double)random()) / RAND_MAX) * (fileInfo->mSize - expected);
  164. seekPos = (seekPos / expected) * expected;
  165. ret = hdfsSeek(ti->hdfs, file, seekPos);
  166. if (ret < 0) {
  167. ret = errno;
  168. fprintf(stderr, "hdfsSeek to %"PRIu64" failed and set"
  169. " errno %d\n", seekPos, ret);
  170. ++nErrs;
  171. continue;
  172. }
  173. ret = hdfsRead(ti->hdfs, file, tmp, expected);
  174. if (ret < 0) {
  175. ret = errno;
  176. fprintf(stderr, "hdfsRead failed and set errno %d\n", ret);
  177. ++nErrs;
  178. continue;
  179. }
  180. if (ret != expected) {
  181. fprintf(stderr, "hdfsRead was supposed to read %d bytes, but "
  182. "it read %d\n", ret, expected);
  183. ++nErrs;
  184. continue;
  185. }
  186. ret = memcmp(content, tmp, expected);
  187. if (ret) {
  188. fprintf(stderr, "hdfsRead result (%.*s) does not match expected (%.*s)",
  189. expected, tmp, expected, content);
  190. ++nErrs;
  191. continue;
  192. }
  193. }
  194. EXPECT_ZERO(hdfsCloseFile(ti->hdfs, file));
  195. fprintf(stderr, "testHdfsMiniStress(threadIdx=%d): finished read loop\n",
  196. ti->threadIdx);
  197. EXPECT_ZERO(nErrs);
  198. return 0;
  199. }
  200. static int testHdfsMiniStressImpl(struct tlhThreadInfo *ti)
  201. {
  202. fprintf(stderr, "testHdfsMiniStress(threadIdx=%d): starting\n",
  203. ti->threadIdx);
  204. EXPECT_NONNULL(ti->hdfs);
  205. // Error injection on, some failures are expected in the read path.
  206. // The expectation is that any memory stomps will cascade and cause
  207. // the following test to fail. Ideally RPC errors would be seperated
  208. // from BlockReader errors (RPC is expected to recover from disconnects).
  209. doTestHdfsMiniStress(ti, 1);
  210. // No error injection
  211. EXPECT_ZERO(doTestHdfsMiniStress(ti, 0));
  212. return 0;
  213. }
  214. static void testHdfsMiniStress(void *v)
  215. {
  216. struct tlhThreadInfo *ti = (struct tlhThreadInfo*)v;
  217. int ret = testHdfsMiniStressImpl(ti);
  218. ti->success = ret;
  219. }
  220. static int checkFailures(struct tlhThreadInfo *ti, int tlhNumThreads)
  221. {
  222. int i, threadsFailed = 0;
  223. const char *sep = "";
  224. for (i = 0; i < tlhNumThreads; i++) {
  225. if (ti[i].success != 0) {
  226. threadsFailed = 1;
  227. }
  228. }
  229. if (!threadsFailed) {
  230. fprintf(stderr, "testLibHdfsMiniStress: all threads succeeded. SUCCESS.\n");
  231. return EXIT_SUCCESS;
  232. }
  233. fprintf(stderr, "testLibHdfsMiniStress: some threads failed: [");
  234. for (i = 0; i < tlhNumThreads; i++) {
  235. if (ti[i].success != 0) {
  236. fprintf(stderr, "%s%d", sep, i);
  237. sep = ", ";
  238. }
  239. }
  240. fprintf(stderr, "]. FAILURE.\n");
  241. return EXIT_FAILURE;
  242. }
  243. /**
  244. * Test intended to stress libhdfs client with concurrent requests. Currently focused
  245. * on concurrent reads.
  246. */
  247. int main(void)
  248. {
  249. int i, tlhNumThreads;
  250. char *dirNm, *fileNm;
  251. tSize fileSz;
  252. const char *tlhNumThreadsStr, *tlhNumDNsStr;
  253. hdfsFS hdfs = NULL;
  254. struct NativeMiniDfsCluster* tlhCluster;
  255. struct tlhThreadInfo ti[TLH_MAX_THREADS];
  256. struct NativeMiniDfsConf conf = {
  257. 1, /* doFormat */
  258. };
  259. dirNm = "/tlhMiniStressData";
  260. fileNm = "/tlhMiniStressData/file";
  261. fileSz = 2*1024*1024;
  262. tlhNumDNsStr = getenv("TLH_NUM_DNS");
  263. if (!tlhNumDNsStr) {
  264. tlhNumDNsStr = "1";
  265. }
  266. conf.numDataNodes = atoi(tlhNumDNsStr);
  267. if ((conf.numDataNodes <= 0) || (conf.numDataNodes > TLH_MAX_DNS)) {
  268. fprintf(stderr, "testLibHdfsMiniStress: must have a number of datanodes "
  269. "between 1 and %d inclusive, not %d\n",
  270. TLH_MAX_DNS, conf.numDataNodes);
  271. return EXIT_FAILURE;
  272. }
  273. tlhNumThreadsStr = getenv("TLH_NUM_THREADS");
  274. if (!tlhNumThreadsStr) {
  275. tlhNumThreadsStr = "8";
  276. }
  277. tlhNumThreads = atoi(tlhNumThreadsStr);
  278. if ((tlhNumThreads <= 0) || (tlhNumThreads > TLH_MAX_THREADS)) {
  279. fprintf(stderr, "testLibHdfsMiniStress: must have a number of threads "
  280. "between 1 and %d inclusive, not %d\n",
  281. TLH_MAX_THREADS, tlhNumThreads);
  282. return EXIT_FAILURE;
  283. }
  284. memset(&ti[0], 0, sizeof(ti));
  285. for (i = 0; i < tlhNumThreads; i++) {
  286. ti[i].threadIdx = i;
  287. }
  288. tlhCluster = nmdCreate(&conf);
  289. EXPECT_NONNULL(tlhCluster);
  290. EXPECT_ZERO(nmdWaitClusterUp(tlhCluster));
  291. EXPECT_ZERO(hdfsNameNodeConnect(tlhCluster, &hdfs, NULL));
  292. // Single threaded writes for now.
  293. EXPECT_ZERO(hdfsWriteData(hdfs, dirNm, fileNm, fileSz));
  294. // Multi-threaded reads.
  295. for (i = 0; i < tlhNumThreads; i++) {
  296. ti[i].theThread.start = testHdfsMiniStress;
  297. ti[i].theThread.arg = &ti[i];
  298. ti[i].hdfs = hdfs;
  299. ti[i].fileNm = fileNm;
  300. EXPECT_ZERO(threadCreate(&ti[i].theThread));
  301. }
  302. for (i = 0; i < tlhNumThreads; i++) {
  303. EXPECT_ZERO(threadJoin(&ti[i].theThread));
  304. }
  305. EXPECT_ZERO(hdfsDisconnect(hdfs));
  306. EXPECT_ZERO(nmdShutdown(tlhCluster));
  307. nmdFree(tlhCluster);
  308. return checkFailures(ti, tlhNumThreads);
  309. }