2016-09-07 20:19:37 -07:00
|
|
|
/* PLASMA MANAGER: Local to a node, connects to other managers to send and
|
|
|
|
* receive objects from them
|
|
|
|
*
|
|
|
|
* The storage manager listens on its main listening port, and if a request for
|
|
|
|
* transfering an object to another object store comes in, it ships the data
|
|
|
|
* using a new connection to the target object manager. */
|
2016-08-17 12:54:34 -07:00
|
|
|
|
2016-10-14 19:27:17 -07:00
|
|
|
#include <fcntl.h>
|
2016-08-17 12:54:34 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
2016-10-11 17:58:14 -07:00
|
|
|
#include <signal.h>
|
2016-08-17 12:54:34 -07:00
|
|
|
#include <stdlib.h>
|
2016-10-29 17:30:34 -07:00
|
|
|
#include <stdbool.h>
|
2016-08-17 12:54:34 -07:00
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
|
2016-10-03 18:29:18 -07:00
|
|
|
#include "uthash.h"
|
|
|
|
#include "utlist.h"
|
2016-10-18 18:20:59 -07:00
|
|
|
#include "utarray.h"
|
2016-10-03 18:29:18 -07:00
|
|
|
#include "utstring.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include "io.h"
|
2016-11-18 19:57:51 -08:00
|
|
|
#include "net.h"
|
2016-09-07 20:19:37 -07:00
|
|
|
#include "event_loop.h"
|
2016-08-17 12:54:34 -07:00
|
|
|
#include "plasma.h"
|
2016-09-15 15:39:33 -07:00
|
|
|
#include "plasma_client.h"
|
2016-09-05 15:34:11 -07:00
|
|
|
#include "plasma_manager.h"
|
2016-10-18 18:20:59 -07:00
|
|
|
#include "state/db.h"
|
|
|
|
#include "state/object_table.h"
|
|
|
|
|
|
|
|
typedef struct client_object_connection client_object_connection;
|
2016-08-17 12:54:34 -07:00
|
|
|
|
2016-10-29 17:30:34 -07:00
|
|
|
/* Entry of the hashtable of objects that are available locally. */
|
|
|
|
typedef struct {
|
|
|
|
/** Object id of this object. */
|
|
|
|
object_id object_id;
|
|
|
|
/** Handle for the uthash table. */
|
|
|
|
UT_hash_handle hh;
|
|
|
|
} available_object;
|
|
|
|
|
2016-10-28 11:56:16 -07:00
|
|
|
struct plasma_manager_state {
|
2016-10-18 18:20:59 -07:00
|
|
|
/** Event loop. */
|
|
|
|
event_loop *loop;
|
2016-10-03 18:29:18 -07:00
|
|
|
/** Connection to the local plasma store for reading or writing data. */
|
2016-10-18 18:20:59 -07:00
|
|
|
plasma_connection *plasma_conn;
|
|
|
|
/** Hash table of all contexts for active connections to
|
|
|
|
* other plasma managers. These are used for writing data to
|
|
|
|
* other plasma stores. */
|
2016-10-03 18:29:18 -07:00
|
|
|
client_connection *manager_connections;
|
2016-10-18 18:20:59 -07:00
|
|
|
db_handle *db;
|
|
|
|
/** Our address. */
|
|
|
|
uint8_t addr[4];
|
|
|
|
/** Our port. */
|
|
|
|
int port;
|
|
|
|
/** Hash table of outstanding fetch requests. The key is
|
|
|
|
* object id, value is a list of connections to the clients
|
|
|
|
* who are blocking on a fetch of this object. */
|
|
|
|
client_object_connection *fetch_connections;
|
2016-10-29 17:30:34 -07:00
|
|
|
/** Initialize an empty hash map for the cache of local available object. */
|
|
|
|
available_object *local_available_objects;
|
2016-10-28 11:56:16 -07:00
|
|
|
};
|
2016-09-07 20:19:37 -07:00
|
|
|
|
2016-10-18 18:20:59 -07:00
|
|
|
plasma_manager_state *g_manager_state = NULL;
|
|
|
|
|
|
|
|
/* The context for fetch and wait requests. These are per client, per object. */
|
|
|
|
struct client_object_connection {
|
|
|
|
/** The ID of the object we are fetching or waiting for. */
|
|
|
|
object_id object_id;
|
|
|
|
/** The client connection context, shared between other
|
|
|
|
* client_object_connections for the same client. */
|
|
|
|
client_connection *client_conn;
|
|
|
|
/** The ID for the timer that will time out the current request to the state
|
|
|
|
* database or another plasma manager. */
|
|
|
|
int64_t timer;
|
|
|
|
/** How many retries we have left for the request. Decremented on every
|
|
|
|
* timeout. */
|
|
|
|
int num_retries;
|
|
|
|
/** Handle for a linked list. */
|
|
|
|
client_object_connection *next;
|
|
|
|
/** Pointer to the array containing the manager locations of
|
|
|
|
* this object. */
|
|
|
|
char **manager_vector;
|
|
|
|
/** The number of manager locations in the array manager_vector. */
|
|
|
|
int manager_count;
|
2016-10-28 11:56:16 -07:00
|
|
|
/** The next manager we should try to contact. This is set to an index in
|
|
|
|
* manager_vector in the retry handler, in case the current attempt fails to
|
|
|
|
* contact a manager. */
|
|
|
|
int next_manager;
|
2016-10-18 18:20:59 -07:00
|
|
|
/** Handle for the uthash table in the client connection
|
|
|
|
* context that keeps track of active object connection
|
|
|
|
* contexts. */
|
|
|
|
UT_hash_handle active_hh;
|
|
|
|
/** Handle for the uthash table in the manager state that
|
|
|
|
* keeps track of outstanding fetch requests. */
|
|
|
|
UT_hash_handle fetch_hh;
|
2016-10-03 18:29:18 -07:00
|
|
|
};
|
2016-09-05 15:34:11 -07:00
|
|
|
|
2016-10-03 18:29:18 -07:00
|
|
|
/* Context for a client connection to another plasma manager. */
|
|
|
|
struct client_connection {
|
2016-10-18 18:20:59 -07:00
|
|
|
/** Current state for this plasma manager. This is shared
|
|
|
|
* between all client connections to the plasma manager. */
|
2016-10-03 18:29:18 -07:00
|
|
|
plasma_manager_state *manager_state;
|
2016-10-18 18:20:59 -07:00
|
|
|
/** Current position in the buffer. */
|
2016-10-03 18:29:18 -07:00
|
|
|
int64_t cursor;
|
2016-10-18 18:20:59 -07:00
|
|
|
/** Buffer that this connection is reading from. If this is a connection to
|
|
|
|
* write data to another plasma store, then it is a linked
|
|
|
|
* list of buffers to write. */
|
|
|
|
/* TODO(swang): Split into two queues, data transfers and data requests. */
|
|
|
|
plasma_request_buffer *transfer_queue;
|
|
|
|
/** File descriptor for the socket connected to the other
|
|
|
|
* plasma manager. */
|
2016-10-03 18:29:18 -07:00
|
|
|
int fd;
|
2016-10-29 17:30:34 -07:00
|
|
|
/** Timer id for timing out wait (or fetch). */
|
|
|
|
int64_t timer_id;
|
|
|
|
/** True if this client is in a "wait" and false if it is in a "fetch". */
|
|
|
|
bool is_wait;
|
|
|
|
/** If this client is processing a wait, this contains the object ids that
|
|
|
|
* are already available. */
|
|
|
|
plasma_reply *wait_reply;
|
2016-10-18 18:20:59 -07:00
|
|
|
/** The objects that we are waiting for and their callback
|
|
|
|
* contexts, for either a fetch or a wait operation. */
|
|
|
|
client_object_connection *active_objects;
|
|
|
|
/** The number of objects that we have left to return for
|
|
|
|
* this fetch or wait operation. */
|
|
|
|
int num_return_objects;
|
|
|
|
/** Fields specific to connections to plasma managers. Key that uniquely
|
|
|
|
* identifies the plasma manager that we're connected to. We will use the
|
|
|
|
* string <address>:<port> as an identifier. */
|
2016-10-03 18:29:18 -07:00
|
|
|
char *ip_addr_port;
|
|
|
|
/** Handle for the uthash table. */
|
2016-10-28 11:56:16 -07:00
|
|
|
UT_hash_handle manager_hh;
|
2016-10-03 18:29:18 -07:00
|
|
|
};
|
2016-08-17 12:54:34 -07:00
|
|
|
|
2016-10-18 18:20:59 -07:00
|
|
|
void free_client_object_connection(client_object_connection *object_conn) {
|
|
|
|
for (int i = 0; i < object_conn->manager_count; ++i) {
|
|
|
|
free(object_conn->manager_vector[i]);
|
|
|
|
}
|
|
|
|
free(object_conn->manager_vector);
|
|
|
|
free(object_conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
int send_client_reply(client_connection *conn, plasma_reply *reply) {
|
|
|
|
CHECK(conn->num_return_objects >= 0);
|
2016-10-28 11:56:16 -07:00
|
|
|
--conn->num_return_objects;
|
2016-10-18 18:20:59 -07:00
|
|
|
/* TODO(swang): Handle errors in write. */
|
|
|
|
int n = write(conn->fd, (uint8_t *) reply, sizeof(plasma_reply));
|
|
|
|
return (n != sizeof(plasma_reply));
|
|
|
|
}
|
|
|
|
|
2016-10-29 15:22:33 -07:00
|
|
|
int send_client_failure_reply(object_id object_id, client_connection *conn) {
|
2016-10-29 17:30:34 -07:00
|
|
|
plasma_reply reply = {
|
|
|
|
.object_ids = {object_id}, .num_object_ids = 1, .has_object = 0};
|
2016-10-29 15:22:33 -07:00
|
|
|
return send_client_reply(conn, &reply);
|
|
|
|
}
|
|
|
|
|
2016-10-18 18:20:59 -07:00
|
|
|
/**
|
|
|
|
* Get the context for the given object ID for the given client
|
|
|
|
* connection, if there is one active.
|
|
|
|
*
|
|
|
|
* @param client_conn The client connection context.
|
|
|
|
* @param object_id The object ID whose context we want.
|
|
|
|
* @return A pointer to the active object context, or NULL if
|
|
|
|
* there isn't one.
|
|
|
|
*/
|
|
|
|
client_object_connection *get_object_connection(client_connection *client_conn,
|
|
|
|
object_id object_id) {
|
|
|
|
client_object_connection *object_conn;
|
|
|
|
HASH_FIND(active_hh, client_conn->active_objects, &object_id,
|
|
|
|
sizeof(object_id), object_conn);
|
|
|
|
return object_conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
client_object_connection *add_object_connection(client_connection *client_conn,
|
|
|
|
object_id object_id) {
|
|
|
|
/* Create a new context for this client connection and object. */
|
|
|
|
client_object_connection *object_conn =
|
|
|
|
malloc(sizeof(client_object_connection));
|
|
|
|
if (!object_conn) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
object_conn->object_id = object_id;
|
|
|
|
object_conn->client_conn = client_conn;
|
|
|
|
object_conn->manager_count = 0;
|
|
|
|
object_conn->manager_vector = NULL;
|
2016-10-28 11:56:16 -07:00
|
|
|
object_conn->next_manager = 0;
|
2016-10-18 18:20:59 -07:00
|
|
|
/* Register the object context with the client context. */
|
|
|
|
HASH_ADD(active_hh, client_conn->active_objects, object_id, sizeof(object_id),
|
|
|
|
object_conn);
|
|
|
|
/* Register the object context with the manager state. */
|
|
|
|
client_object_connection *fetch_connections;
|
|
|
|
HASH_FIND(fetch_hh, client_conn->manager_state->fetch_connections, &object_id,
|
|
|
|
sizeof(object_id), fetch_connections);
|
|
|
|
LOG_DEBUG("Registering fd %d for fetch.", client_conn->fd);
|
|
|
|
if (!fetch_connections) {
|
|
|
|
fetch_connections = NULL;
|
|
|
|
LL_APPEND(fetch_connections, object_conn);
|
|
|
|
HASH_ADD(fetch_hh, client_conn->manager_state->fetch_connections, object_id,
|
|
|
|
sizeof(object_id), fetch_connections);
|
|
|
|
} else {
|
|
|
|
LL_APPEND(fetch_connections, object_conn);
|
|
|
|
}
|
|
|
|
return object_conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_object_connection(client_connection *client_conn,
|
|
|
|
client_object_connection *object_conn) {
|
|
|
|
/* Deregister the object context with the client context. */
|
|
|
|
HASH_DELETE(active_hh, client_conn->active_objects, object_conn);
|
|
|
|
/* Deregister the object context with the manager state. */
|
|
|
|
client_object_connection *object_conns;
|
|
|
|
HASH_FIND(fetch_hh, client_conn->manager_state->fetch_connections,
|
|
|
|
&(object_conn->object_id), sizeof(object_conn->object_id),
|
|
|
|
object_conns);
|
|
|
|
CHECK(object_conns);
|
|
|
|
int len;
|
|
|
|
client_object_connection *tmp;
|
|
|
|
LL_COUNT(object_conns, tmp, len);
|
|
|
|
if (len == 1) {
|
|
|
|
HASH_DELETE(fetch_hh, client_conn->manager_state->fetch_connections,
|
|
|
|
object_conns);
|
|
|
|
}
|
|
|
|
LL_DELETE(object_conns, object_conn);
|
|
|
|
/* Free the object. */
|
|
|
|
free_client_object_connection(object_conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
plasma_manager_state *init_plasma_manager_state(const char *store_socket_name,
|
|
|
|
const char *manager_addr,
|
|
|
|
int manager_port,
|
|
|
|
const char *db_addr,
|
|
|
|
int db_port) {
|
2016-10-03 18:29:18 -07:00
|
|
|
plasma_manager_state *state = malloc(sizeof(plasma_manager_state));
|
2016-10-18 18:20:59 -07:00
|
|
|
state->loop = event_loop_create();
|
2016-11-06 17:31:14 -08:00
|
|
|
state->plasma_conn =
|
|
|
|
plasma_connect(store_socket_name, NULL, PLASMA_DEFAULT_RELEASE_DELAY);
|
2016-10-03 18:29:18 -07:00
|
|
|
state->manager_connections = NULL;
|
2016-10-18 18:20:59 -07:00
|
|
|
state->fetch_connections = NULL;
|
|
|
|
if (db_addr) {
|
|
|
|
state->db = db_connect(db_addr, db_port, "plasma_manager", manager_addr,
|
|
|
|
manager_port);
|
|
|
|
db_attach(state->db, state->loop);
|
|
|
|
} else {
|
|
|
|
state->db = NULL;
|
|
|
|
LOG_DEBUG("No db connection specified");
|
|
|
|
}
|
|
|
|
sscanf(manager_addr, "%hhu.%hhu.%hhu.%hhu", &state->addr[0], &state->addr[1],
|
|
|
|
&state->addr[2], &state->addr[3]);
|
|
|
|
state->port = manager_port;
|
2016-10-29 17:30:34 -07:00
|
|
|
/* Initialize an empty hash map for the cache of local available objects. */
|
|
|
|
state->local_available_objects = NULL;
|
|
|
|
/* Subscribe to notifications about sealed objects. */
|
|
|
|
int plasma_fd = plasma_subscribe(state->plasma_conn);
|
|
|
|
/* Add the callback that processes the notification to the event loop. */
|
|
|
|
event_loop_add_file(state->loop, plasma_fd, EVENT_LOOP_READ,
|
|
|
|
process_object_notification, state);
|
2016-10-03 18:29:18 -07:00
|
|
|
return state;
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
|
|
|
|
2016-10-28 11:56:16 -07:00
|
|
|
void destroy_plasma_manager_state(plasma_manager_state *state) {
|
|
|
|
client_connection *manager_conn, *tmp;
|
|
|
|
HASH_ITER(manager_hh, state->manager_connections, manager_conn, tmp) {
|
|
|
|
HASH_DELETE(manager_hh, state->manager_connections, manager_conn);
|
|
|
|
plasma_request_buffer *head = manager_conn->transfer_queue;
|
|
|
|
while (head) {
|
|
|
|
LL_DELETE(manager_conn->transfer_queue, head);
|
|
|
|
free(head);
|
|
|
|
head = manager_conn->transfer_queue;
|
|
|
|
}
|
|
|
|
close(manager_conn->fd);
|
|
|
|
free(manager_conn->ip_addr_port);
|
|
|
|
free(manager_conn);
|
|
|
|
}
|
2016-10-29 15:22:33 -07:00
|
|
|
|
|
|
|
if (state->fetch_connections != NULL) {
|
|
|
|
LOG_DEBUG("There were outstanding fetch requests.");
|
|
|
|
client_object_connection *object_conn, *tmp;
|
|
|
|
HASH_ITER(fetch_hh, state->fetch_connections, object_conn, tmp) {
|
|
|
|
remove_object_connection(object_conn->client_conn, object_conn);
|
|
|
|
}
|
|
|
|
}
|
2016-10-28 11:56:16 -07:00
|
|
|
|
2016-11-06 17:31:14 -08:00
|
|
|
plasma_disconnect(state->plasma_conn);
|
2016-10-28 11:56:16 -07:00
|
|
|
event_loop_destroy(state->loop);
|
|
|
|
free(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
event_loop *get_event_loop(plasma_manager_state *state) {
|
|
|
|
return state->loop;
|
|
|
|
}
|
|
|
|
|
2016-09-07 20:19:37 -07:00
|
|
|
/* Handle a command request that came in through a socket (transfering data,
|
|
|
|
* or accepting incoming data). */
|
2016-10-03 18:29:18 -07:00
|
|
|
void process_message(event_loop *loop,
|
|
|
|
int client_sock,
|
|
|
|
void *context,
|
|
|
|
int events);
|
|
|
|
|
2016-10-18 18:20:59 -07:00
|
|
|
void write_object_chunk(client_connection *conn, plasma_request_buffer *buf) {
|
2016-10-28 11:56:16 -07:00
|
|
|
LOG_DEBUG("Writing data to fd %d", conn->fd);
|
2016-08-17 12:54:34 -07:00
|
|
|
ssize_t r, s;
|
2016-10-03 18:29:18 -07:00
|
|
|
/* Try to write one BUFSIZE at a time. */
|
|
|
|
s = buf->data_size + buf->metadata_size - conn->cursor;
|
|
|
|
if (s > BUFSIZE)
|
|
|
|
s = BUFSIZE;
|
|
|
|
r = write(conn->fd, buf->data + conn->cursor, s);
|
|
|
|
|
|
|
|
if (r != s) {
|
|
|
|
if (r > 0) {
|
2016-11-15 20:33:29 -08:00
|
|
|
LOG_ERROR("partial write on fd %d", conn->fd);
|
2016-09-08 15:28:27 -07:00
|
|
|
} else {
|
2016-11-10 18:13:26 -08:00
|
|
|
/* TODO(swang): This should not be a fatal error, since connections can
|
|
|
|
* close at any time. */
|
|
|
|
LOG_FATAL("write error");
|
2016-09-08 15:28:27 -07:00
|
|
|
}
|
2016-10-03 18:29:18 -07:00
|
|
|
} else {
|
|
|
|
conn->cursor += r;
|
|
|
|
}
|
|
|
|
if (r == 0) {
|
2016-10-18 18:20:59 -07:00
|
|
|
/* If we've finished writing this buffer, reset the cursor to zero. */
|
|
|
|
LOG_DEBUG("writing on channel %d finished", conn->fd);
|
2016-10-03 18:29:18 -07:00
|
|
|
conn->cursor = 0;
|
2016-10-21 00:47:34 -07:00
|
|
|
/* We are done sending the object, so release it. The corresponding call to
|
|
|
|
* plasma_get occurred in process_transfer_request. */
|
|
|
|
plasma_release(conn->manager_state->plasma_conn, buf->object_id);
|
2016-10-18 18:20:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void send_queued_request(event_loop *loop,
|
|
|
|
int data_sock,
|
|
|
|
void *context,
|
|
|
|
int events) {
|
|
|
|
client_connection *conn = (client_connection *) context;
|
|
|
|
if (conn->transfer_queue == NULL) {
|
|
|
|
/* If there are no objects to transfer, temporarily remove this connection
|
|
|
|
* from the event loop. It will be reawoken when we receive another
|
|
|
|
* PLASMA_TRANSFER request. */
|
|
|
|
event_loop_remove_file(loop, conn->fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
plasma_request_buffer *buf = conn->transfer_queue;
|
|
|
|
plasma_request manager_req = make_plasma_request(buf->object_id);
|
|
|
|
switch (buf->type) {
|
|
|
|
case PLASMA_TRANSFER:
|
|
|
|
memcpy(manager_req.addr, conn->manager_state->addr,
|
|
|
|
sizeof(manager_req.addr));
|
|
|
|
manager_req.port = conn->manager_state->port;
|
|
|
|
plasma_send_request(conn->fd, buf->type, &manager_req);
|
|
|
|
break;
|
|
|
|
case PLASMA_DATA:
|
|
|
|
LOG_DEBUG("Transferring object to manager");
|
|
|
|
if (conn->cursor == 0) {
|
|
|
|
/* If the cursor is zero, we haven't sent any requests for this object
|
|
|
|
* yet,
|
|
|
|
* so send the initial PLASMA_DATA request. */
|
|
|
|
manager_req.data_size = buf->data_size;
|
|
|
|
manager_req.metadata_size = buf->metadata_size;
|
|
|
|
plasma_send_request(conn->fd, PLASMA_DATA, &manager_req);
|
|
|
|
}
|
|
|
|
write_object_chunk(conn, buf);
|
|
|
|
break;
|
|
|
|
default:
|
2016-11-10 18:13:26 -08:00
|
|
|
LOG_FATAL("Buffered request has unknown type.");
|
2016-10-18 18:20:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We are done sending this request. */
|
|
|
|
if (conn->cursor == 0) {
|
2016-10-03 18:29:18 -07:00
|
|
|
LL_DELETE(conn->transfer_queue, buf);
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 11:56:16 -07:00
|
|
|
int read_object_chunk(client_connection *conn, plasma_request_buffer *buf) {
|
|
|
|
LOG_DEBUG("Reading data from fd %d to %p", conn->fd,
|
|
|
|
buf->data + conn->cursor);
|
2016-10-03 18:29:18 -07:00
|
|
|
ssize_t r, s;
|
|
|
|
CHECK(buf != NULL);
|
|
|
|
/* Try to read one BUFSIZE at a time. */
|
|
|
|
s = buf->data_size + buf->metadata_size - conn->cursor;
|
|
|
|
if (s > BUFSIZE) {
|
|
|
|
s = BUFSIZE;
|
|
|
|
}
|
2016-10-28 11:56:16 -07:00
|
|
|
r = read(conn->fd, buf->data + conn->cursor, s);
|
2016-10-03 18:29:18 -07:00
|
|
|
|
|
|
|
if (r == -1) {
|
2016-11-15 20:33:29 -08:00
|
|
|
LOG_ERROR("read error");
|
2016-10-03 18:29:18 -07:00
|
|
|
} else if (r == 0) {
|
|
|
|
LOG_DEBUG("end of file");
|
|
|
|
} else {
|
|
|
|
conn->cursor += r;
|
|
|
|
}
|
2016-10-28 11:56:16 -07:00
|
|
|
/* If the cursor is equal to the full object size, reset the cursor and we're
|
|
|
|
* done. */
|
|
|
|
if (conn->cursor == buf->data_size + buf->metadata_size) {
|
|
|
|
conn->cursor = 0;
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2016-10-18 18:20:59 -07:00
|
|
|
|
2016-10-28 11:56:16 -07:00
|
|
|
void process_data_chunk(event_loop *loop,
|
|
|
|
int data_sock,
|
|
|
|
void *context,
|
|
|
|
int events) {
|
|
|
|
/* Read the object chunk. */
|
|
|
|
client_connection *conn = (client_connection *) context;
|
|
|
|
plasma_request_buffer *buf = conn->transfer_queue;
|
|
|
|
int done = read_object_chunk(conn, buf);
|
|
|
|
if (!done) {
|
2016-10-18 18:20:59 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:47:34 -07:00
|
|
|
/* Seal the object and release it. The release corresponds to the call to
|
|
|
|
* plasma_create that occurred in process_data_request. */
|
2016-10-18 18:20:59 -07:00
|
|
|
LOG_DEBUG("reading on channel %d finished", data_sock);
|
2016-10-29 17:30:34 -07:00
|
|
|
/* The following seal also triggers notification of clients for fetch or
|
|
|
|
* wait requests, see process_object_notification. */
|
2016-10-18 18:20:59 -07:00
|
|
|
plasma_seal(conn->manager_state->plasma_conn, buf->object_id);
|
2016-10-21 00:47:34 -07:00
|
|
|
plasma_release(conn->manager_state->plasma_conn, buf->object_id);
|
2016-10-18 18:20:59 -07:00
|
|
|
/* Remove the request buffer used for reading this object's data. */
|
|
|
|
LL_DELETE(conn->transfer_queue, buf);
|
|
|
|
free(buf);
|
|
|
|
/* Switch to listening for requests from this socket, instead of reading
|
|
|
|
* object data. */
|
|
|
|
event_loop_remove_file(loop, data_sock);
|
|
|
|
event_loop_add_file(loop, data_sock, EVENT_LOOP_READ, process_message, conn);
|
2016-10-03 18:29:18 -07:00
|
|
|
}
|
|
|
|
|
2016-10-18 18:20:59 -07:00
|
|
|
client_connection *get_manager_connection(plasma_manager_state *state,
|
|
|
|
const char *ip_addr,
|
|
|
|
int port) {
|
|
|
|
/* TODO(swang): Should probably check whether ip_addr and port belong to us.
|
|
|
|
*/
|
|
|
|
UT_string *ip_addr_port;
|
|
|
|
utstring_new(ip_addr_port);
|
|
|
|
utstring_printf(ip_addr_port, "%s:%d", ip_addr, port);
|
|
|
|
client_connection *manager_conn;
|
2016-10-28 11:56:16 -07:00
|
|
|
HASH_FIND(manager_hh, state->manager_connections, utstring_body(ip_addr_port),
|
|
|
|
utstring_len(ip_addr_port), manager_conn);
|
2016-10-18 18:20:59 -07:00
|
|
|
if (!manager_conn) {
|
|
|
|
/* If we don't already have a connection to this manager, start one. */
|
2016-10-28 11:56:16 -07:00
|
|
|
int fd = plasma_manager_connect(ip_addr, port);
|
|
|
|
/* TODO(swang): Handle the case when connection to this manager was
|
|
|
|
* unsuccessful. */
|
|
|
|
CHECK(fd >= 0);
|
2016-10-18 18:20:59 -07:00
|
|
|
manager_conn = malloc(sizeof(client_connection));
|
2016-10-28 11:56:16 -07:00
|
|
|
manager_conn->fd = fd;
|
2016-10-18 18:20:59 -07:00
|
|
|
manager_conn->manager_state = state;
|
|
|
|
manager_conn->transfer_queue = NULL;
|
|
|
|
manager_conn->cursor = 0;
|
|
|
|
manager_conn->ip_addr_port = strdup(utstring_body(ip_addr_port));
|
2016-10-28 11:56:16 -07:00
|
|
|
HASH_ADD_KEYPTR(manager_hh,
|
|
|
|
manager_conn->manager_state->manager_connections,
|
2016-10-18 18:20:59 -07:00
|
|
|
manager_conn->ip_addr_port,
|
|
|
|
strlen(manager_conn->ip_addr_port), manager_conn);
|
|
|
|
}
|
|
|
|
utstring_free(ip_addr_port);
|
|
|
|
return manager_conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
void process_transfer_request(event_loop *loop,
|
|
|
|
object_id object_id,
|
|
|
|
uint8_t addr[4],
|
|
|
|
int port,
|
|
|
|
client_connection *conn) {
|
2016-10-03 18:29:18 -07:00
|
|
|
uint8_t *data;
|
|
|
|
int64_t data_size;
|
|
|
|
uint8_t *metadata;
|
|
|
|
int64_t metadata_size;
|
2016-10-18 18:20:59 -07:00
|
|
|
/* TODO(swang): A non-blocking plasma_get, or else we could block here
|
|
|
|
* forever if we don't end up sealing this object. */
|
2016-10-21 00:47:34 -07:00
|
|
|
/* The corresponding call to plasma_release will happen in
|
|
|
|
* write_object_chunk. */
|
2016-10-18 18:20:59 -07:00
|
|
|
plasma_get(conn->manager_state->plasma_conn, object_id, &data_size, &data,
|
2016-10-03 18:29:18 -07:00
|
|
|
&metadata_size, &metadata);
|
|
|
|
assert(metadata == data + data_size);
|
2016-10-18 18:20:59 -07:00
|
|
|
plasma_request_buffer *buf = malloc(sizeof(plasma_request_buffer));
|
|
|
|
buf->type = PLASMA_DATA;
|
2016-10-03 18:29:18 -07:00
|
|
|
buf->object_id = object_id;
|
|
|
|
buf->data = data; /* We treat this as a pointer to the
|
|
|
|
concatenated data and metadata. */
|
|
|
|
buf->data_size = data_size;
|
|
|
|
buf->metadata_size = metadata_size;
|
|
|
|
|
|
|
|
UT_string *ip_addr;
|
|
|
|
utstring_new(ip_addr);
|
|
|
|
utstring_printf(ip_addr, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
|
2016-10-18 18:20:59 -07:00
|
|
|
client_connection *manager_conn =
|
|
|
|
get_manager_connection(conn->manager_state, utstring_body(ip_addr), port);
|
2016-10-03 18:29:18 -07:00
|
|
|
utstring_free(ip_addr);
|
|
|
|
|
|
|
|
if (manager_conn->transfer_queue == NULL) {
|
|
|
|
/* If we already have a connection to this manager and its inactive,
|
|
|
|
* (re)register it with the event loop again. */
|
|
|
|
event_loop_add_file(loop, manager_conn->fd, EVENT_LOOP_WRITE,
|
2016-10-18 18:20:59 -07:00
|
|
|
send_queued_request, manager_conn);
|
2016-10-03 18:29:18 -07:00
|
|
|
}
|
|
|
|
/* Add this transfer request to this connection's transfer queue. */
|
|
|
|
LL_APPEND(manager_conn->transfer_queue, buf);
|
|
|
|
}
|
|
|
|
|
2016-10-18 18:20:59 -07:00
|
|
|
void process_data_request(event_loop *loop,
|
|
|
|
int client_sock,
|
|
|
|
object_id object_id,
|
|
|
|
int64_t data_size,
|
|
|
|
int64_t metadata_size,
|
|
|
|
client_connection *conn) {
|
|
|
|
plasma_request_buffer *buf = malloc(sizeof(plasma_request_buffer));
|
2016-10-03 18:29:18 -07:00
|
|
|
buf->object_id = object_id;
|
|
|
|
buf->data_size = data_size;
|
|
|
|
buf->metadata_size = metadata_size;
|
|
|
|
|
2016-10-21 00:47:34 -07:00
|
|
|
/* The corresponding call to plasma_release should happen in
|
|
|
|
* process_data_chunk. */
|
2016-10-18 18:20:59 -07:00
|
|
|
plasma_create(conn->manager_state->plasma_conn, object_id, data_size, NULL,
|
2016-10-03 18:29:18 -07:00
|
|
|
metadata_size, &(buf->data));
|
|
|
|
LL_APPEND(conn->transfer_queue, buf);
|
2016-10-28 11:56:16 -07:00
|
|
|
CHECK(conn->cursor == 0);
|
2016-10-03 18:29:18 -07:00
|
|
|
|
|
|
|
/* Switch to reading the data from this socket, instead of listening for
|
|
|
|
* other requests. */
|
|
|
|
event_loop_remove_file(loop, client_sock);
|
2016-10-18 18:20:59 -07:00
|
|
|
event_loop_add_file(loop, client_sock, EVENT_LOOP_READ, process_data_chunk,
|
2016-10-03 18:29:18 -07:00
|
|
|
conn);
|
|
|
|
}
|
|
|
|
|
2016-10-18 18:20:59 -07:00
|
|
|
/**
|
|
|
|
* Request a transfer for the given object ID from the next manager believed to
|
|
|
|
* have a copy. Adds the request for this object ID to the queue of outgoing
|
|
|
|
* requests to the manager we want to try.
|
|
|
|
*
|
|
|
|
* @param client_conn The context for the connection to this client.
|
|
|
|
* @param object_id The object ID we want to request a transfer of.
|
|
|
|
* @returns Void.
|
|
|
|
*/
|
|
|
|
void request_transfer_from(client_connection *client_conn,
|
|
|
|
object_id object_id) {
|
|
|
|
client_object_connection *object_conn =
|
|
|
|
get_object_connection(client_conn, object_id);
|
|
|
|
CHECK(object_conn);
|
|
|
|
CHECK(object_conn->manager_count > 0);
|
2016-10-28 11:56:16 -07:00
|
|
|
CHECK(object_conn->next_manager >= 0 &&
|
|
|
|
object_conn->next_manager < object_conn->manager_count);
|
2016-10-18 18:20:59 -07:00
|
|
|
char addr[16];
|
|
|
|
int port;
|
2016-10-28 11:56:16 -07:00
|
|
|
parse_ip_addr_port(object_conn->manager_vector[object_conn->next_manager],
|
|
|
|
addr, &port);
|
2016-10-18 18:20:59 -07:00
|
|
|
|
|
|
|
client_connection *manager_conn =
|
|
|
|
get_manager_connection(client_conn->manager_state, addr, port);
|
|
|
|
plasma_request_buffer *transfer_request =
|
|
|
|
malloc(sizeof(plasma_request_buffer));
|
|
|
|
transfer_request->type = PLASMA_TRANSFER;
|
|
|
|
transfer_request->object_id = object_conn->object_id;
|
|
|
|
|
|
|
|
if (manager_conn->transfer_queue == NULL) {
|
|
|
|
/* If we already have a connection to this manager and its inactive,
|
|
|
|
* (re)register it with the event loop. */
|
|
|
|
event_loop_add_file(client_conn->manager_state->loop, manager_conn->fd,
|
|
|
|
EVENT_LOOP_WRITE, send_queued_request, manager_conn);
|
|
|
|
}
|
|
|
|
/* Add this transfer request to this connection's transfer queue. */
|
|
|
|
LL_APPEND(manager_conn->transfer_queue, transfer_request);
|
2016-10-28 11:56:16 -07:00
|
|
|
/* On the next attempt, try the next manager in manager_vector. */
|
|
|
|
++object_conn->next_manager;
|
|
|
|
object_conn->next_manager %= object_conn->manager_count;
|
2016-10-18 18:20:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int manager_timeout_handler(event_loop *loop, timer_id id, void *context) {
|
|
|
|
client_object_connection *object_conn = context;
|
|
|
|
client_connection *client_conn = object_conn->client_conn;
|
|
|
|
LOG_DEBUG("Timer went off, %d tries left", object_conn->num_retries);
|
|
|
|
if (object_conn->num_retries > 0) {
|
|
|
|
request_transfer_from(client_conn, object_conn->object_id);
|
|
|
|
object_conn->num_retries--;
|
|
|
|
return MANAGER_TIMEOUT;
|
|
|
|
}
|
2016-10-29 17:30:34 -07:00
|
|
|
plasma_reply reply = {.object_ids = {object_conn->object_id},
|
|
|
|
.num_object_ids = 1,
|
|
|
|
.has_object = 0};
|
2016-10-18 18:20:59 -07:00
|
|
|
send_client_reply(client_conn, &reply);
|
|
|
|
remove_object_connection(client_conn, object_conn);
|
2016-10-29 17:30:34 -07:00
|
|
|
return EVENT_LOOP_TIMER_DONE;
|
2016-10-18 18:20:59 -07:00
|
|
|
}
|
|
|
|
|
2016-10-28 11:56:16 -07:00
|
|
|
/* TODO(swang): Consolidate transfer requests for same object
|
|
|
|
* from different client IDs by passing in manager state, not
|
|
|
|
* client context. */
|
2016-10-18 18:20:59 -07:00
|
|
|
void request_transfer(object_id object_id,
|
|
|
|
int manager_count,
|
|
|
|
const char *manager_vector[],
|
|
|
|
void *context) {
|
|
|
|
client_connection *client_conn = (client_connection *) context;
|
|
|
|
client_object_connection *object_conn =
|
|
|
|
get_object_connection(client_conn, object_id);
|
2016-10-29 15:22:33 -07:00
|
|
|
/* If there's already an outstanding fetch for this object for this client,
|
|
|
|
* let the outstanding request finish the work. */
|
|
|
|
if (object_conn) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* If the object isn't on any managers, report a failure to the client. */
|
2016-10-18 18:20:59 -07:00
|
|
|
LOG_DEBUG("Object is on %d managers", manager_count);
|
|
|
|
if (manager_count == 0) {
|
|
|
|
/* TODO(swang): Instead of immediately counting this as a failure, maybe
|
|
|
|
* register a Redis callback for changes to this object table entry. */
|
|
|
|
free(manager_vector);
|
2016-10-29 15:22:33 -07:00
|
|
|
send_client_failure_reply(object_id, client_conn);
|
2016-10-29 17:30:34 -07:00
|
|
|
remove_object_connection(client_conn, object_conn);
|
2016-10-18 18:20:59 -07:00
|
|
|
return;
|
|
|
|
}
|
2016-10-29 15:22:33 -07:00
|
|
|
/* Register the new outstanding fetch with the current client connection. */
|
|
|
|
object_conn = add_object_connection(client_conn, object_id);
|
|
|
|
if (!object_conn) {
|
|
|
|
LOG_DEBUG("Unable to allocate memory for object context.");
|
|
|
|
send_client_failure_reply(object_id, client_conn);
|
|
|
|
}
|
2016-10-18 18:20:59 -07:00
|
|
|
/* Pick a different manager to request a transfer from on every attempt. */
|
|
|
|
object_conn->manager_count = manager_count;
|
|
|
|
object_conn->manager_vector = malloc(manager_count * sizeof(char *));
|
|
|
|
memset(object_conn->manager_vector, 0, manager_count * sizeof(char *));
|
|
|
|
for (int i = 0; i < manager_count; ++i) {
|
|
|
|
int len = strlen(manager_vector[i]);
|
|
|
|
object_conn->manager_vector[i] = malloc(len + 1);
|
|
|
|
strncpy(object_conn->manager_vector[i], manager_vector[i], len);
|
|
|
|
object_conn->manager_vector[i][len] = '\0';
|
|
|
|
}
|
|
|
|
free(manager_vector);
|
|
|
|
/* Wait for the object data for the default number of retries, which timeout
|
|
|
|
* after a default interval. */
|
|
|
|
object_conn->num_retries = NUM_RETRIES;
|
|
|
|
object_conn->timer =
|
|
|
|
event_loop_add_timer(client_conn->manager_state->loop, MANAGER_TIMEOUT,
|
|
|
|
manager_timeout_handler, object_conn);
|
|
|
|
request_transfer_from(client_conn, object_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void process_fetch_request(client_connection *client_conn,
|
|
|
|
object_id object_id) {
|
2016-10-29 17:30:34 -07:00
|
|
|
client_conn->is_wait = false;
|
|
|
|
client_conn->wait_reply = NULL;
|
|
|
|
plasma_reply reply = {.object_ids = {object_id}, .num_object_ids = 1};
|
2016-10-18 18:20:59 -07:00
|
|
|
if (client_conn->manager_state->db == NULL) {
|
|
|
|
reply.has_object = 0;
|
|
|
|
send_client_reply(client_conn, &reply);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Return success immediately if we already have this object. */
|
|
|
|
int is_local = 0;
|
|
|
|
plasma_contains(client_conn->manager_state->plasma_conn, object_id,
|
|
|
|
&is_local);
|
|
|
|
if (is_local) {
|
|
|
|
reply.has_object = 1;
|
|
|
|
send_client_reply(client_conn, &reply);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-29 15:22:33 -07:00
|
|
|
retry_info retry = {
|
|
|
|
.num_retries = NUM_RETRIES,
|
|
|
|
.timeout = MANAGER_TIMEOUT,
|
|
|
|
.fail_callback = (table_fail_callback) send_client_failure_reply,
|
|
|
|
};
|
2016-10-18 18:20:59 -07:00
|
|
|
/* Request a transfer from a plasma manager that has this object. */
|
2016-10-29 15:22:33 -07:00
|
|
|
object_table_lookup(client_conn->manager_state->db, object_id, &retry,
|
2016-10-18 18:20:59 -07:00
|
|
|
request_transfer, client_conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
void process_fetch_requests(client_connection *client_conn,
|
|
|
|
int num_object_ids,
|
|
|
|
object_id object_ids[]) {
|
|
|
|
for (int i = 0; i < num_object_ids; ++i) {
|
2016-10-28 11:56:16 -07:00
|
|
|
++client_conn->num_return_objects;
|
2016-10-18 18:20:59 -07:00
|
|
|
process_fetch_request(client_conn, object_ids[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-29 17:30:34 -07:00
|
|
|
void return_from_wait(client_connection *client_conn) {
|
|
|
|
CHECK(client_conn->is_wait);
|
|
|
|
int64_t size =
|
|
|
|
sizeof(plasma_reply) +
|
|
|
|
(client_conn->wait_reply->num_object_ids - 1) * sizeof(object_id);
|
|
|
|
client_conn->wait_reply->num_objects_returned =
|
|
|
|
client_conn->wait_reply->num_object_ids - client_conn->num_return_objects;
|
|
|
|
int n = write(client_conn->fd, (uint8_t *) client_conn->wait_reply, size);
|
|
|
|
CHECK(n == size);
|
|
|
|
free(client_conn->wait_reply);
|
|
|
|
/* Clean the remaining object connections. */
|
|
|
|
client_object_connection *object_conn, *tmp;
|
|
|
|
HASH_ITER(active_hh, client_conn->active_objects, object_conn, tmp) {
|
|
|
|
remove_object_connection(client_conn, object_conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int wait_timeout_handler(event_loop *loop, timer_id id, void *context) {
|
|
|
|
client_connection *client_conn = context;
|
|
|
|
CHECK(client_conn->timer_id == id);
|
|
|
|
return_from_wait(client_conn);
|
|
|
|
return EVENT_LOOP_TIMER_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void process_wait_request(client_connection *client_conn,
|
|
|
|
int num_object_ids,
|
|
|
|
object_id object_ids[],
|
|
|
|
uint64_t timeout,
|
|
|
|
int num_returns) {
|
|
|
|
plasma_manager_state *manager_state = client_conn->manager_state;
|
|
|
|
client_conn->num_return_objects = num_returns;
|
|
|
|
client_conn->is_wait = true;
|
|
|
|
client_conn->timer_id = event_loop_add_timer(
|
|
|
|
manager_state->loop, timeout, wait_timeout_handler, client_conn);
|
|
|
|
int64_t size = sizeof(plasma_reply) + (num_returns - 1) * sizeof(object_id);
|
|
|
|
client_conn->wait_reply = malloc(size);
|
|
|
|
memset(client_conn->wait_reply, 0, size);
|
|
|
|
client_conn->wait_reply->num_object_ids = num_returns;
|
|
|
|
for (int i = 0; i < num_object_ids; ++i) {
|
|
|
|
available_object *entry;
|
|
|
|
HASH_FIND(hh, manager_state->local_available_objects, &object_ids[i],
|
|
|
|
sizeof(object_id), entry);
|
|
|
|
if (entry) {
|
|
|
|
/* If an object id occurs twice in object_ids, this will count them twice.
|
|
|
|
* This might not be desirable behavior. */
|
|
|
|
client_conn->num_return_objects -= 1;
|
|
|
|
client_conn->wait_reply->object_ids[client_conn->num_return_objects] =
|
|
|
|
entry->object_id;
|
|
|
|
if (client_conn->num_return_objects == 0) {
|
|
|
|
event_loop_remove_timer(manager_state->loop, client_conn->timer_id);
|
|
|
|
return_from_wait(client_conn);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
add_object_connection(client_conn, object_ids[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void process_object_notification(event_loop *loop,
|
|
|
|
int client_sock,
|
|
|
|
void *context,
|
|
|
|
int events) {
|
|
|
|
plasma_manager_state *state = context;
|
|
|
|
object_id obj_id;
|
|
|
|
/* Read the notification from Plasma. */
|
|
|
|
int n = recv(client_sock, &obj_id, sizeof(object_id), MSG_WAITALL);
|
|
|
|
if (n == 0) {
|
|
|
|
/* The store has closed the socket. */
|
|
|
|
LOG_DEBUG("The plasma store has closed the object notification socket.");
|
|
|
|
event_loop_remove_file(loop, client_sock);
|
|
|
|
close(client_sock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
CHECK(n == sizeof(object_id));
|
|
|
|
/* Add object to locally available object. */
|
|
|
|
/* TODO(pcm): Where is this deallocated? */
|
|
|
|
available_object *entry =
|
|
|
|
(available_object *) malloc(sizeof(available_object));
|
|
|
|
entry->object_id = obj_id;
|
|
|
|
HASH_ADD(hh, state->local_available_objects, object_id, sizeof(object_id),
|
|
|
|
entry);
|
|
|
|
/* Notify any clients who were waiting on a fetch to this object and tick
|
|
|
|
* off objects we are waiting for. */
|
|
|
|
client_object_connection *object_conn, *next;
|
|
|
|
client_connection *client_conn;
|
|
|
|
HASH_FIND(fetch_hh, state->fetch_connections, &obj_id, sizeof(object_id),
|
|
|
|
object_conn);
|
|
|
|
plasma_reply reply = {
|
|
|
|
.object_ids = {obj_id}, .num_object_ids = 1, .has_object = 1};
|
|
|
|
while (object_conn) {
|
|
|
|
next = object_conn->next;
|
|
|
|
client_conn = object_conn->client_conn;
|
|
|
|
if (!client_conn->is_wait) {
|
|
|
|
event_loop_remove_timer(state->loop, object_conn->timer);
|
|
|
|
send_client_reply(client_conn, &reply);
|
|
|
|
} else {
|
|
|
|
client_conn->num_return_objects -= 1;
|
|
|
|
client_conn->wait_reply->object_ids[client_conn->num_return_objects] =
|
|
|
|
obj_id;
|
|
|
|
if (client_conn->num_return_objects == 0) {
|
|
|
|
event_loop_remove_timer(loop, client_conn->timer_id);
|
|
|
|
return_from_wait(client_conn);
|
|
|
|
object_conn = next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
remove_object_connection(client_conn, object_conn);
|
|
|
|
object_conn = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-03 18:29:18 -07:00
|
|
|
void process_message(event_loop *loop,
|
|
|
|
int client_sock,
|
|
|
|
void *context,
|
|
|
|
int events) {
|
|
|
|
client_connection *conn = (client_connection *) context;
|
|
|
|
|
|
|
|
int64_t type;
|
|
|
|
int64_t length;
|
|
|
|
plasma_request *req;
|
|
|
|
read_message(client_sock, &type, &length, (uint8_t **) &req);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case PLASMA_TRANSFER:
|
2016-10-18 18:20:59 -07:00
|
|
|
process_transfer_request(loop, req->object_ids[0], req->addr, req->port,
|
|
|
|
conn);
|
2016-09-08 15:28:27 -07:00
|
|
|
break;
|
2016-10-03 18:29:18 -07:00
|
|
|
case PLASMA_DATA:
|
2016-10-18 18:20:59 -07:00
|
|
|
LOG_DEBUG("Starting to stream data");
|
|
|
|
process_data_request(loop, client_sock, req->object_ids[0], req->data_size,
|
|
|
|
req->metadata_size, conn);
|
|
|
|
break;
|
|
|
|
case PLASMA_FETCH:
|
|
|
|
LOG_DEBUG("Processing fetch");
|
|
|
|
process_fetch_requests(conn, req->num_object_ids, req->object_ids);
|
|
|
|
break;
|
2016-10-29 17:30:34 -07:00
|
|
|
case PLASMA_WAIT:
|
|
|
|
LOG_DEBUG("Processing wait");
|
|
|
|
process_wait_request(conn, req->num_object_ids, req->object_ids,
|
|
|
|
req->timeout, req->num_returns);
|
2016-11-11 17:05:40 -08:00
|
|
|
break;
|
2016-11-18 19:57:51 -08:00
|
|
|
case PLASMA_SEAL: {
|
2016-10-29 15:22:33 -07:00
|
|
|
/* TODO(swang): Log the error if we fail to add the object, and possibly
|
|
|
|
* retry later? */
|
|
|
|
retry_info retry = {
|
|
|
|
.num_retries = NUM_RETRIES,
|
|
|
|
.timeout = MANAGER_TIMEOUT,
|
|
|
|
.fail_callback = NULL,
|
|
|
|
};
|
|
|
|
object_table_add(conn->manager_state->db, req->object_ids[0], &retry, NULL,
|
|
|
|
NULL);
|
2016-11-18 19:57:51 -08:00
|
|
|
} break;
|
2016-10-03 18:29:18 -07:00
|
|
|
case DISCONNECT_CLIENT: {
|
|
|
|
LOG_INFO("Disconnecting client on fd %d", client_sock);
|
2016-10-18 18:20:59 -07:00
|
|
|
/* TODO(swang): Check if this connection was to a plasma manager. If so,
|
|
|
|
* delete it. */
|
2016-10-03 18:29:18 -07:00
|
|
|
event_loop_remove_file(loop, client_sock);
|
|
|
|
close(client_sock);
|
|
|
|
free(conn);
|
|
|
|
} break;
|
2016-09-08 15:28:27 -07:00
|
|
|
default:
|
2016-11-10 18:13:26 -08:00
|
|
|
LOG_FATAL("invalid request %" PRId64, type);
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
2016-10-03 18:29:18 -07:00
|
|
|
|
|
|
|
free(req);
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
|
|
|
|
2016-10-28 21:26:54 -07:00
|
|
|
/* TODO(pcm): Split this into two methods: new_worker_connection
|
|
|
|
* and new_manager_connection and also split client_connection
|
|
|
|
* into two structs, one for workers and one for other plasma managers. */
|
2016-10-28 11:56:16 -07:00
|
|
|
client_connection *new_client_connection(event_loop *loop,
|
|
|
|
int listener_sock,
|
|
|
|
void *context,
|
|
|
|
int events) {
|
2016-10-03 18:29:18 -07:00
|
|
|
int new_socket = accept_client(listener_sock);
|
|
|
|
/* Create a new data connection context per client. */
|
|
|
|
client_connection *conn = malloc(sizeof(client_connection));
|
|
|
|
conn->manager_state = (plasma_manager_state *) context;
|
2016-10-28 11:56:16 -07:00
|
|
|
conn->cursor = 0;
|
2016-10-03 18:29:18 -07:00
|
|
|
conn->transfer_queue = NULL;
|
2016-10-18 18:20:59 -07:00
|
|
|
conn->fd = new_socket;
|
|
|
|
conn->active_objects = NULL;
|
|
|
|
conn->num_return_objects = 0;
|
2016-10-03 18:29:18 -07:00
|
|
|
event_loop_add_file(loop, new_socket, EVENT_LOOP_READ, process_message, conn);
|
2016-10-28 21:26:54 -07:00
|
|
|
LOG_DEBUG("New client connection with fd %d", new_socket);
|
2016-10-28 11:56:16 -07:00
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_new_client(event_loop *loop,
|
|
|
|
int listener_sock,
|
|
|
|
void *context,
|
|
|
|
int events) {
|
|
|
|
(void) new_client_connection(loop, listener_sock, context, events);
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_client_sock(client_connection *conn) {
|
|
|
|
return conn->fd;
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
|
|
|
|
2016-09-15 15:39:33 -07:00
|
|
|
void start_server(const char *store_socket_name,
|
2016-10-28 21:26:54 -07:00
|
|
|
const char *manager_socket_name,
|
2016-09-15 15:39:33 -07:00
|
|
|
const char *master_addr,
|
2016-10-18 18:20:59 -07:00
|
|
|
int port,
|
|
|
|
const char *db_addr,
|
|
|
|
int db_port) {
|
2016-11-02 00:09:04 -07:00
|
|
|
/* Bind the sockets before we try to connect to the plasma store.
|
|
|
|
* In case the bind does not succeed, we want to be able to exit
|
|
|
|
* without breaking the pipe to the store. */
|
|
|
|
int remote_sock = bind_inet_sock(port, false);
|
2016-10-31 15:00:15 -07:00
|
|
|
if (remote_sock < 0) {
|
|
|
|
exit(EXIT_COULD_NOT_BIND_PORT);
|
|
|
|
}
|
2016-11-02 00:09:04 -07:00
|
|
|
|
|
|
|
int local_sock = bind_ipc_sock(manager_socket_name, false);
|
2016-10-28 21:26:54 -07:00
|
|
|
CHECKM(local_sock >= 0, "Unable to bind local manager socket");
|
2016-10-28 11:56:16 -07:00
|
|
|
|
2016-11-02 00:09:04 -07:00
|
|
|
g_manager_state = init_plasma_manager_state(store_socket_name, master_addr,
|
|
|
|
port, db_addr, db_port);
|
|
|
|
CHECK(g_manager_state);
|
|
|
|
|
|
|
|
CHECK(listen(remote_sock, 5) != -1);
|
|
|
|
CHECK(listen(local_sock, 5) != -1);
|
|
|
|
|
2016-10-18 18:20:59 -07:00
|
|
|
LOG_DEBUG("Started server connected to store %s, listening on port %d",
|
|
|
|
store_socket_name, port);
|
2016-10-28 21:26:54 -07:00
|
|
|
event_loop_add_file(g_manager_state->loop, local_sock, EVENT_LOOP_READ,
|
|
|
|
handle_new_client, g_manager_state);
|
|
|
|
event_loop_add_file(g_manager_state->loop, remote_sock, EVENT_LOOP_READ,
|
2016-10-28 11:56:16 -07:00
|
|
|
handle_new_client, g_manager_state);
|
2016-10-18 18:20:59 -07:00
|
|
|
event_loop_run(g_manager_state->loop);
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
|
|
|
|
2016-10-11 17:58:14 -07:00
|
|
|
/* Report "success" to valgrind. */
|
|
|
|
void signal_handler(int signal) {
|
|
|
|
if (signal == SIGTERM) {
|
2016-10-18 18:20:59 -07:00
|
|
|
if (g_manager_state) {
|
|
|
|
db_disconnect(g_manager_state->db);
|
|
|
|
}
|
2016-10-11 17:58:14 -07:00
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 11:56:16 -07:00
|
|
|
/* Only declare the main function if we are not in testing mode, since the test
|
|
|
|
* suite has its own declaration of main. */
|
|
|
|
#ifndef PLASMA_TEST
|
2016-09-15 15:39:33 -07:00
|
|
|
int main(int argc, char *argv[]) {
|
2016-10-11 17:58:14 -07:00
|
|
|
signal(SIGTERM, signal_handler);
|
2016-09-07 20:19:37 -07:00
|
|
|
/* Socket name of the plasma store this manager is connected to. */
|
2016-09-15 15:39:33 -07:00
|
|
|
char *store_socket_name = NULL;
|
2016-10-28 21:26:54 -07:00
|
|
|
/* Socket name this manager will bind to. */
|
|
|
|
char *manager_socket_name = NULL;
|
2016-09-07 20:19:37 -07:00
|
|
|
/* IP address of this node. */
|
2016-09-15 15:39:33 -07:00
|
|
|
char *master_addr = NULL;
|
2016-09-07 20:19:37 -07:00
|
|
|
/* Port number the manager should use. */
|
2016-10-28 21:26:54 -07:00
|
|
|
int port = -1;
|
2016-10-18 18:20:59 -07:00
|
|
|
/* IP address and port of state database. */
|
|
|
|
char *db_host = NULL;
|
2016-08-17 12:54:34 -07:00
|
|
|
int c;
|
2016-10-28 21:26:54 -07:00
|
|
|
while ((c = getopt(argc, argv, "s:m:h:p:r:")) != -1) {
|
2016-08-17 12:54:34 -07:00
|
|
|
switch (c) {
|
|
|
|
case 's':
|
|
|
|
store_socket_name = optarg;
|
|
|
|
break;
|
|
|
|
case 'm':
|
2016-10-28 21:26:54 -07:00
|
|
|
manager_socket_name = optarg;
|
|
|
|
break;
|
|
|
|
case 'h':
|
2016-08-17 12:54:34 -07:00
|
|
|
master_addr = optarg;
|
|
|
|
break;
|
2016-08-22 15:30:16 -07:00
|
|
|
case 'p':
|
|
|
|
port = atoi(optarg);
|
|
|
|
break;
|
2016-10-28 21:26:54 -07:00
|
|
|
case 'r':
|
2016-10-18 18:20:59 -07:00
|
|
|
db_host = optarg;
|
|
|
|
break;
|
2016-08-17 12:54:34 -07:00
|
|
|
default:
|
2016-11-10 18:13:26 -08:00
|
|
|
LOG_FATAL("unknown option %c", c);
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!store_socket_name) {
|
2016-11-10 18:13:26 -08:00
|
|
|
LOG_FATAL(
|
2016-09-08 15:28:27 -07:00
|
|
|
"please specify socket for connecting to the plasma store with -s "
|
|
|
|
"switch");
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
2016-10-28 21:26:54 -07:00
|
|
|
if (!manager_socket_name) {
|
2016-11-10 18:13:26 -08:00
|
|
|
LOG_FATAL(
|
2016-10-28 21:26:54 -07:00
|
|
|
"please specify socket name of the manager's local socket with -m "
|
|
|
|
"switch");
|
|
|
|
}
|
2016-08-17 12:54:34 -07:00
|
|
|
if (!master_addr) {
|
2016-11-10 18:13:26 -08:00
|
|
|
LOG_FATAL(
|
2016-09-08 15:28:27 -07:00
|
|
|
"please specify ip address of the current host in the format "
|
2016-10-31 15:00:15 -07:00
|
|
|
"123.456.789.10 with -h switch");
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
2016-10-28 21:26:54 -07:00
|
|
|
if (port == -1) {
|
2016-11-10 18:13:26 -08:00
|
|
|
LOG_FATAL(
|
2016-10-28 21:26:54 -07:00
|
|
|
"please specify port the plasma manager shall listen to in the"
|
|
|
|
"format 12345 with -p switch");
|
|
|
|
}
|
2016-10-18 18:20:59 -07:00
|
|
|
char db_addr[16];
|
|
|
|
int db_port;
|
|
|
|
if (db_host) {
|
|
|
|
parse_ip_addr_port(db_host, db_addr, &db_port);
|
2016-10-28 21:26:54 -07:00
|
|
|
start_server(store_socket_name, manager_socket_name, master_addr, port,
|
|
|
|
db_addr, db_port);
|
2016-10-18 18:20:59 -07:00
|
|
|
} else {
|
2016-10-28 21:26:54 -07:00
|
|
|
start_server(store_socket_name, manager_socket_name, master_addr, port,
|
|
|
|
NULL, 0);
|
2016-10-18 18:20:59 -07:00
|
|
|
}
|
2016-08-17 12:54:34 -07:00
|
|
|
}
|
2016-10-28 11:56:16 -07:00
|
|
|
#endif
|