Skip to content

Commit 743571a

Browse files
authored
Merge pull request #207 from DenverM80/put_dir_test
Fix ds3_connection_pool queue behavior
2 parents 878c7d3 + 7783a2d commit 743571a

File tree

11 files changed

+208
-51
lines changed

11 files changed

+208
-51
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@ Debug/
4343
*.ilk
4444
*.pdb
4545
ds3.dll
46-
/win32/output/bin
46+
/win32/output/bin
47+
.idea/
48+
cmake-build-debug/
49+
*.dylib

src/ds3.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "ds3.h"
2727
#include "ds3_net.h"
28+
#include "ds3_connection.h"
2829
#include "ds3_request.h"
2930
#include "ds3_string_multimap_impl.h"
3031
#include "ds3_utils.h"

src/ds3_connection.c

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,35 @@
2020
#include <curl/curl.h>
2121
#include <glib.h>
2222
#include <inttypes.h>
23-
#include "ds3_net.h"
24-
23+
#include "ds3_connection.h"
24+
25+
//-- Opaque struct
26+
struct _ds3_connection_pool{
27+
ds3_connection** connections;
28+
uint16_t num_connections; // the number of connections created
29+
ds3_connection** connection_queue;
30+
uint16_t max_connections; // max number of possible connections, which the connections and queue arrays will be initialized to
31+
int queue_head;
32+
int queue_tail;
33+
ds3_mutex mutex;
34+
ds3_condition available_connection_notifier;
35+
uint16_t ref_count;
36+
};
2537

2638
ds3_connection_pool* ds3_connection_pool_init(void) {
27-
return ds3_connection_pool_init_with_size(CONNECTION_POOL_SIZE);
39+
return ds3_connection_pool_init_with_size(DEFAULT_CONNECTION_POOL_SIZE);
2840
}
2941

3042
ds3_connection_pool* ds3_connection_pool_init_with_size(uint16_t pool_size) {
3143
ds3_connection_pool* pool = g_new0(ds3_connection_pool, 1);
44+
3245
pool->connections = g_new0(ds3_connection*, pool_size);
33-
pool->num_connections = pool_size;
46+
pool->connection_queue = g_new0(ds3_connection*, pool_size);
47+
48+
pool->max_connections = pool_size;
49+
3450
g_mutex_init(&pool->mutex);
35-
g_cond_init(&pool->available_connections);
51+
g_cond_init(&pool->available_connection_notifier);
3652
pool->ref_count = 1;
3753
return pool;
3854
}
@@ -55,35 +71,39 @@ void ds3_connection_pool_clear(ds3_connection_pool* pool, ds3_bool already_locke
5571
}
5672

5773
g_free(pool->connections);
74+
g_free(pool->connection_queue);
5875
g_mutex_unlock(&pool->mutex);
5976
g_mutex_clear(&pool->mutex); // an attempt to clear a locked mutex is undefined
60-
g_cond_clear(&pool->available_connections);
77+
g_cond_clear(&pool->available_connection_notifier);
6178
}
6279

63-
static int _pool_inc(int index, uint16_t num_connections) {
64-
return (index+1) % num_connections;
80+
static int _queue_inc(int index, uint16_t size) {
81+
return (index+1) % size;
6582
}
6683

67-
static int _pool_full(ds3_connection_pool* pool) {
68-
return (_pool_inc(pool->head, pool->num_connections) == pool->tail);
84+
static int _queue_is_empty(ds3_connection_pool* pool) {
85+
int queue_head = pool->queue_head;
86+
return pool->queue_tail == queue_head && pool->connection_queue[queue_head] == NULL;
6987
}
7088

7189
ds3_connection* ds3_connection_acquire(ds3_connection_pool* pool) {
7290
ds3_connection* connection = NULL;
7391

7492
g_mutex_lock(&pool->mutex);
75-
while (_pool_full(pool)) {
76-
g_cond_wait(&pool->available_connections, &pool->mutex);
93+
while (_queue_is_empty(pool) && pool->num_connections >= pool->max_connections) {
94+
g_cond_wait(&pool->available_connection_notifier, &pool->mutex);
7795
}
7896

79-
if (pool->connections[pool->head] == NULL) {
97+
if (_queue_is_empty(pool)) {
8098
connection = curl_easy_init();
8199

82-
pool->connections[pool->head] = connection;
100+
pool->connections[pool->num_connections] = connection;
101+
pool->num_connections++;
83102
} else {
84-
connection = pool->connections[pool->head];
103+
connection = pool->connection_queue[pool->queue_tail];
104+
pool->connection_queue[pool->queue_tail] = NULL;
105+
pool->queue_tail = _queue_inc(pool->queue_tail, pool->max_connections);
85106
}
86-
pool->head = _pool_inc(pool->head, pool->num_connections);
87107

88108
g_mutex_unlock(&pool->mutex);
89109

@@ -94,10 +114,13 @@ void ds3_connection_release(ds3_connection_pool* pool, ds3_connection* connectio
94114
g_mutex_lock(&pool->mutex);
95115

96116
curl_easy_reset(connection);
97-
pool->tail = _pool_inc(pool->tail, pool->num_connections);
98117

118+
pool->connection_queue[pool->queue_head] = connection;
119+
120+
pool->queue_head = _queue_inc(pool->queue_head, pool->max_connections);
121+
122+
g_cond_signal(&pool->available_connection_notifier);
99123
g_mutex_unlock(&pool->mutex);
100-
g_cond_signal(&pool->available_connections);
101124
}
102125

103126
void ds3_connection_pool_inc_ref(ds3_connection_pool* pool) {

src/ds3_connection.h

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,16 @@ extern "C" {
2626

2727
#include <curl/curl.h>
2828
#include <glib.h>
29+
#include "ds3.h"
2930

30-
#define CONNECTION_POOL_SIZE 10
31+
#define DEFAULT_CONNECTION_POOL_SIZE 10
3132

3233
typedef GMutex ds3_mutex;
3334
typedef GCond ds3_condition;
3435

3536
typedef CURL ds3_connection;
3637

37-
//-- Opaque struct
38-
struct _ds3_connection_pool{
39-
ds3_connection** connections;
40-
uint16_t num_connections;
41-
int head;
42-
int tail;
43-
ds3_mutex mutex;
44-
ds3_condition available_connections;
45-
uint16_t ref_count;
46-
};
38+
typedef struct _ds3_connection_pool ds3_connection_pool;
4739

4840
ds3_connection_pool* ds3_connection_pool_init(void);
4941
ds3_connection_pool* ds3_connection_pool_init_with_size(uint16_t pool_size);

src/ds3_net.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@
2020
#include <curl/curl.h>
2121

2222
#include "ds3_request.h"
23-
#include "ds3.h"
2423
#include "ds3_net.h"
2524
#include "ds3_utils.h"
26-
#include "ds3_string_multimap.h"
2725
#include "ds3_string_multimap_impl.h"
2826
#include "ds3_connection.h"
2927

src/ds3_net.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ extern "C" {
2626

2727
#include "ds3.h"
2828
#include "ds3_string_multimap.h"
29-
#include "ds3_connection.h"
3029

3130
char* escape_url(const char* url);
3231
char* escape_url_extended(const char* url, const char** delimiters, uint32_t num_delimiters);

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ add_executable(ds3_c_tests
7777
search_tests.cpp
7878
service_tests.cpp
7979
connection_tests.cpp
80+
put_directory.cpp
8081
test.cpp)
8182

8283
add_test(regression_tests ds3_c_tests)

test/connection_tests.cpp

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE( ds3_client_create_free ) {
2525
printf("-----Testing ds3_client create and free-------\n");
2626

2727
ds3_client* client = get_client();
28-
BOOST_CHECK_EQUAL(client->connection_pool->ref_count, 1);
28+
BOOST_CHECK(client->connection_pool != NULL);
2929
ds3_creds_free(client->creds);
3030
ds3_client_free(client);
3131
}
@@ -34,10 +34,8 @@ BOOST_AUTO_TEST_CASE( ds3_connection_pool_copy ) {
3434
printf("-----Testing ds3_copy_client-------\n");
3535

3636
ds3_client* client = get_client();
37-
BOOST_CHECK_EQUAL(client->connection_pool->ref_count, 1);
3837

3938
ds3_client* client_copy = ds3_copy_client(client);
40-
BOOST_CHECK_EQUAL(client->connection_pool->ref_count, 2);
4139
BOOST_CHECK_EQUAL(client->endpoint->value, client_copy->endpoint->value);
4240
if (client->proxy) {
4341
BOOST_CHECK_EQUAL(client->proxy->value, client_copy->proxy->value);
@@ -53,7 +51,7 @@ BOOST_AUTO_TEST_CASE( ds3_connection_pool_copy ) {
5351
ds3_creds_free(client->creds);
5452
ds3_client_free(client);
5553

56-
BOOST_CHECK_EQUAL(client_copy->connection_pool->ref_count, 1);
54+
BOOST_CHECK(client_copy->connection_pool != NULL);
5755
ds3_creds_free(client_copy->creds);
5856
ds3_client_free(client_copy);
5957
}
@@ -63,18 +61,18 @@ BOOST_AUTO_TEST_CASE( create_bucket_with_copied_client ) {
6361

6462
ds3_client* client = get_client();
6563
ds3_connection_pool* cp = client->connection_pool;
66-
BOOST_CHECK_EQUAL(cp->ref_count, 1);
64+
BOOST_CHECK(cp != NULL);
6765

6866
ds3_client* client_copy = ds3_copy_client(client);
69-
BOOST_CHECK_EQUAL(cp->ref_count, 2);
67+
BOOST_CHECK_EQUAL(cp, client_copy->connection_pool);
7068

7169
const char* client_bucket_name = "create_bucket_from_original_client";
7270
ds3_error* error = create_bucket_with_data_policy(client, client_bucket_name, ids.data_policy_id->value);
7371
handle_error(error);
7472
clear_bucket(client, client_bucket_name);
7573
ds3_creds_free(client->creds);
7674
ds3_client_free(client);
77-
BOOST_CHECK_EQUAL(cp->ref_count, 1);
75+
BOOST_CHECK(client_copy->connection_pool != NULL);
7876

7977
const char* copied_client_bucket_name = "create_bucket_from_copied_client";
8078
error = create_bucket_with_data_policy(client_copy, copied_client_bucket_name, ids.data_policy_id->value);
@@ -162,7 +160,7 @@ BOOST_AUTO_TEST_CASE( bulk_put_200_very_small_files_multithreaded ) {
162160

163161
ds3_master_object_list_response* chunk_response = ensure_available_chunks(client, bulk_response->job_id);
164162

165-
GPtrArray* put_objs_args_array = new_put_chunks_threads_args(client, object_name, bucket_name, bulk_response, chunk_response, num_threads, False);
163+
GPtrArray* put_objs_args_array = new_put_chunks_threads_args(client, object_name, NULL, bucket_name, bulk_response, chunk_response, num_threads, False);
166164

167165
GThread* chunks_thread_0 = g_thread_new("objects_0", (GThreadFunc)put_chunks_from_file, g_ptr_array_index(put_objs_args_array, 0));
168166
GThread* chunks_thread_1 = g_thread_new("objects_1", (GThreadFunc)put_chunks_from_file, g_ptr_array_index(put_objs_args_array, 1));
@@ -204,7 +202,7 @@ BOOST_AUTO_TEST_CASE( sequential_vs_parallel_xfer ) {
204202

205203
ds3_master_object_list_response* sequential_chunks = ensure_available_chunks(client, mol->job_id);
206204

207-
GPtrArray* put_sequential_objs_threads_array = new_put_chunks_threads_args(client, obj_name, sequential_bucket_name, mol, sequential_chunks, 1, False);
205+
GPtrArray* put_sequential_objs_threads_array = new_put_chunks_threads_args(client, obj_name, NULL, sequential_bucket_name, mol, sequential_chunks, 1, False);
208206

209207
// capture sequential test start time
210208
clock_gettime(CLOCK_MONOTONIC, &start_time_t);
@@ -238,7 +236,7 @@ BOOST_AUTO_TEST_CASE( sequential_vs_parallel_xfer ) {
238236

239237
ds3_master_object_list_response* parallel_chunks = ensure_available_chunks(client, mol->job_id);
240238

241-
GPtrArray* put_parallel_objs_threads_array = new_put_chunks_threads_args(client, obj_name, parallel_bucket_name, mol, parallel_chunks, 4, False);
239+
GPtrArray* put_parallel_objs_threads_array = new_put_chunks_threads_args(client, obj_name, NULL, parallel_bucket_name, mol, parallel_chunks, 4, False);
242240

243241
// capture sequential test start time
244242
clock_gettime(CLOCK_MONOTONIC, &start_time_t);
@@ -315,8 +313,8 @@ BOOST_AUTO_TEST_CASE( multiple_client_xfer ) {
315313
ds3_master_object_list_response* client1_chunks = ensure_available_chunks(client1, mol1->job_id);
316314
ds3_master_object_list_response* client2_chunks = ensure_available_chunks(client2, mol2->job_id);
317315

318-
GPtrArray* client1_put_objs_args = new_put_chunks_threads_args(client1, obj_name, client1_bucket_name, mol1, client1_chunks, 1, True);
319-
GPtrArray* client2_put_objs_args = new_put_chunks_threads_args(client2, obj_name, client2_bucket_name, mol2, client2_chunks, 1, True);
316+
GPtrArray* client1_put_objs_args = new_put_chunks_threads_args(client1, obj_name, NULL, client1_bucket_name, mol1, client1_chunks, 1, True);
317+
GPtrArray* client2_put_objs_args = new_put_chunks_threads_args(client2, obj_name, NULL, client2_bucket_name, mol2, client2_chunks, 1, True);
320318

321319
// capture sequential test start time
322320
clock_gettime(CLOCK_MONOTONIC, &start_time_t);
@@ -407,9 +405,9 @@ BOOST_AUTO_TEST_CASE( performance_bulk_put ) {
407405
ds3_master_object_list_response* chunks_response2 = ensure_available_chunks(client2, bulk_response2->job_id);
408406
ds3_master_object_list_response* chunks_response3 = ensure_available_chunks(client3, bulk_response3->job_id);
409407

410-
GPtrArray* put_perf_objs_threads_array1 = new_put_chunks_threads_args(client1, obj_prefix, bucket_name1, bulk_response1, chunks_response1, 1, True);
411-
GPtrArray* put_perf_objs_threads_array2 = new_put_chunks_threads_args(client2, obj_prefix, bucket_name2, bulk_response2, chunks_response2, 1, True);
412-
GPtrArray* put_perf_objs_threads_array3 = new_put_chunks_threads_args(client3, obj_prefix, bucket_name3, bulk_response3, chunks_response3, 1, True);
408+
GPtrArray* put_perf_objs_threads_array1 = new_put_chunks_threads_args(client1, obj_prefix, NULL, bucket_name1, bulk_response1, chunks_response1, 1, True);
409+
GPtrArray* put_perf_objs_threads_array2 = new_put_chunks_threads_args(client2, obj_prefix, NULL, bucket_name2, bulk_response2, chunks_response2, 1, True);
410+
GPtrArray* put_perf_objs_threads_array3 = new_put_chunks_threads_args(client3, obj_prefix, NULL, bucket_name3, bulk_response3, chunks_response3, 1, True);
413411

414412
// capture sequential test start time
415413
struct timespec start_time_t, end_time_t;

test/put_directory.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* ******************************************************************************
3+
* Copyright 2014-2016 Spectra Logic Corporation. All Rights Reserved.
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
5+
* this file except in compliance with the License. A copy of the License is located at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* or in the "license" file accompanying this file.
10+
* This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11+
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
12+
* specific language governing permissions and limitations under the License.
13+
* ****************************************************************************
14+
*/
15+
16+
#include <stdio.h>
17+
#include <string.h>
18+
#include <unistd.h>
19+
#include <glib.h>
20+
#include <sys/stat.h>
21+
#include <boost/test/unit_test.hpp>
22+
#include <inttypes.h>
23+
#include "ds3.h"
24+
#include "ds3_net.h"
25+
#include "ds3_utils.h"
26+
#include "test.h"
27+
28+
BOOST_AUTO_TEST_CASE( put_directory_4_threads) {
29+
printf("-----Testing PUT all objects in a directory with 4 threads-------\n");
30+
31+
const char* dir_path = getenv("DS3_TEST_DIRECTORY");
32+
if (dir_path == NULL) {
33+
printf("ENV[DS3_TEST_DIRECTORY] unset - Skipping put_directory_4_threads test.\n");
34+
return;
35+
}
36+
37+
const char* bucket_name = "test_bulk_put_directory";
38+
printf(" Putting all files in [%s] to bucket [%s]\n", dir_path, bucket_name);
39+
40+
ds3_client* client = get_client_at_loglvl(DS3_DEBUG);
41+
int client_thread=1;
42+
ds3_client_register_logging(client, DS3_DEBUG, test_log, (void*)&client_thread); // Use DEBUG level logging
43+
44+
ds3_error* error = create_bucket_with_data_policy(client, bucket_name, ids.data_policy_id->value);
45+
46+
char* objects_list[100];
47+
uint64_t num_objs = 0;
48+
GDir* dir_info = g_dir_open(dir_path, 0, NULL);
49+
for (char* current_obj = (char*)g_dir_read_name(dir_info); current_obj != NULL; current_obj = (char*)g_dir_read_name(dir_info)) {
50+
objects_list[num_objs++] = current_obj;
51+
printf(" obj[%" PRIu64 "][%s]\n", num_objs, objects_list[num_objs-1]);
52+
}
53+
54+
ds3_bulk_object_list_response* bulk_object_list = ds3_convert_file_list_with_basepath((const char**)objects_list, num_objs, dir_path);
55+
56+
ds3_request* request = ds3_init_put_bulk_job_spectra_s3_request(bucket_name, bulk_object_list);
57+
ds3_master_object_list_response* mol;
58+
error = ds3_put_bulk_job_spectra_s3_request(client, request, &mol);
59+
ds3_request_free(request);
60+
ds3_bulk_object_list_response_free(bulk_object_list);
61+
handle_error(error);
62+
63+
// Allocate cache
64+
ds3_master_object_list_response* chunks_list = ensure_available_chunks(client, mol->job_id);
65+
66+
// Use helper functions from test.cpp
67+
const uint8_t num_threads = 4;
68+
GPtrArray* put_dir_args = new_put_chunks_threads_args(client, NULL, dir_path, bucket_name, mol, chunks_list, num_threads, True); // Last param indicates verbose logging in the spawned thread
69+
70+
71+
// capture test start time
72+
struct timespec start_time_t, end_time_t;
73+
double elapsed_t;
74+
clock_gettime(CLOCK_MONOTONIC, &start_time_t);
75+
76+
GThread* put_dir_xfer_thread_0 = g_thread_new("put_dir_xfer_thread_0", (GThreadFunc)put_chunks_from_file, g_ptr_array_index(put_dir_args, 0));
77+
GThread* put_dir_xfer_thread_1 = g_thread_new("put_dir_xfer_thread_1", (GThreadFunc)put_chunks_from_file, g_ptr_array_index(put_dir_args, 1));
78+
GThread* put_dir_xfer_thread_2 = g_thread_new("put_dir_xfer_thread_2", (GThreadFunc)put_chunks_from_file, g_ptr_array_index(put_dir_args, 2));
79+
GThread* put_dir_xfer_thread_3 = g_thread_new("put_dir_xfer_thread_3", (GThreadFunc)put_chunks_from_file, g_ptr_array_index(put_dir_args, 3));
80+
81+
// Block and cleanup GThread(s)
82+
g_thread_join(put_dir_xfer_thread_0);
83+
g_thread_join(put_dir_xfer_thread_1);
84+
g_thread_join(put_dir_xfer_thread_2);
85+
g_thread_join(put_dir_xfer_thread_3);
86+
87+
// find elapsed CPU and real time
88+
clock_gettime(CLOCK_MONOTONIC, &end_time_t);
89+
elapsed_t = timespec_to_seconds(&end_time_t) - timespec_to_seconds(&start_time_t);
90+
ds3_log_message(client->log, DS3_INFO, " Elapsed time[%f]", elapsed_t);
91+
92+
g_dir_close(dir_info);
93+
ds3_master_object_list_response_free(chunks_list);
94+
ds3_master_object_list_response_free(mol);
95+
put_chunks_threads_args_free(put_dir_args);
96+
clear_bucket(client, bucket_name);
97+
free_client(client);
98+
}

0 commit comments

Comments
 (0)