commit
555215ab77
@ -0,0 +1 @@
|
||||
*.wasm
|
@ -0,0 +1,24 @@
|
||||
include ../wasm_apps/common.mk
|
||||
|
||||
.PHONY: all
|
||||
all: \
|
||||
scratch_storage_get.wasm \
|
||||
scratch_storage_set.wasm \
|
||||
scratch_storage_delete.wasm \
|
||||
scratch_storage_upsert.wasm \
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -f scratch_storage_set.wa* scratch_storage_get.wa* scratch_storage_delete.wa* scratch_storage_upsert.wa*
|
||||
|
||||
scratch_storage_set.wasm: scratch_storage_set.c
|
||||
@${WASMCC} ${WASMCFLAGS} ${WASMLDFLAGS} $^ -o $@
|
||||
|
||||
scratch_storage_get.wasm: scratch_storage_get.c
|
||||
@${WASMCC} ${WASMCFLAGS} ${WASMLDFLAGS} $^ -o $@
|
||||
|
||||
scratch_storage_delete.wasm: scratch_storage_delete.c
|
||||
@${WASMCC} ${WASMCFLAGS} ${WASMLDFLAGS} $^ -o $@
|
||||
|
||||
scratch_storage_upsert.wasm: scratch_storage_upsert.c
|
||||
@${WASMCC} ${WASMCFLAGS} ${WASMLDFLAGS} $^ -o $@
|
@ -0,0 +1,32 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern int scratch_storage_delete(void *key, uint32_t key_len)
|
||||
__attribute__((__import_module__("scratch_storage"), __import_name__("delete")));
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "%s <key>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *key = argv[1];
|
||||
|
||||
if (key == NULL || strlen(key) < 0) {
|
||||
fprintf(stderr, "%s <key>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rc = scratch_storage_delete(key, strlen(key));
|
||||
if (rc == 1) {
|
||||
printf("Key '%s' not found\n", key);
|
||||
return 0;
|
||||
} else {
|
||||
printf("Key %s deleted\n", key);
|
||||
}
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern int scratch_storage_get(void *key, uint32_t key_len, void *buf, uint32_t buf_len)
|
||||
__attribute__((__import_module__("scratch_storage"), __import_name__("get")));
|
||||
|
||||
extern uint32_t scratch_storage_get_size(void *key, uint32_t key_len)
|
||||
__attribute__((__import_module__("scratch_storage"), __import_name__("get_size")));
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "%s <key>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *key = argv[1];
|
||||
|
||||
if (key == NULL || strlen(key) < 0) {
|
||||
fprintf(stderr, "%s <key>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t val_size = scratch_storage_get_size(key, strlen(key));
|
||||
char *buf = calloc(val_size + 1, sizeof(char));
|
||||
int rc = scratch_storage_get(key, strlen(key), buf, val_size);
|
||||
assert(rc != 2);
|
||||
if (rc == 1) {
|
||||
printf("Key '%s' not found\n", key);
|
||||
return 0;
|
||||
} else {
|
||||
printf("Key %s resolved to value of size %u with contents %s\n", key, val_size, buf);
|
||||
}
|
||||
};
|
@ -0,0 +1,35 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern int scratch_storage_set(void *key, uint32_t key_len, void *value, uint32_t value_len)
|
||||
__attribute__((__import_module__("scratch_storage"), __import_name__("set")));
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "%s <key> <value>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *key = argv[1];
|
||||
char *value = argv[2];
|
||||
|
||||
if (key == NULL || strlen(key) < 0 || value == NULL || strlen(value) < 0) {
|
||||
fprintf(stderr, "%s <key> <value>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rc = scratch_storage_set(key, strlen(key), value, strlen(value));
|
||||
if (rc == 1) {
|
||||
printf("Key %s was already present\n", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(rc == 0);
|
||||
printf("Key %s set to value %s\n", key, value);
|
||||
return rc;
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern int scratch_storage_upsert(void *key, uint32_t key_len, void *value, uint32_t value_len)
|
||||
__attribute__((__import_module__("scratch_storage"), __import_name__("upsert")));
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "%s <key> <value>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *key = argv[1];
|
||||
char *value = argv[2];
|
||||
|
||||
if (key == NULL || strlen(key) < 0 || value == NULL || strlen(value) < 0) {
|
||||
fprintf(stderr, "%s <key> <value>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
scratch_storage_upsert(key, strlen(key), value, strlen(value));
|
||||
printf("Key %s set to value %s\n", key, value);
|
||||
};
|
@ -1 +1 @@
|
||||
Subproject commit 456edf5cecd7edc36314ba87e2d76ebf8cf1b5db
|
||||
Subproject commit 20a7c88816c8f8882e03d42c76ff8c1e72bfeaec
|
@ -0,0 +1,65 @@
|
||||
#include <stdint.h>
|
||||
#include "sledge_abi.h"
|
||||
|
||||
#define INLINE __attribute__((always_inline))
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @param key_len
|
||||
* @returns value_size at key or 0 if key not present
|
||||
*/
|
||||
INLINE uint32_t
|
||||
scratch_storage_get_size(uint32_t key_offset, uint32_t key_len)
|
||||
{
|
||||
return sledge_abi__scratch_storage_get_size(key_offset, key_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key_offset
|
||||
* @param key_len
|
||||
* @param buf_offset linear memory offset to buffer where value should be copied.
|
||||
* @param buf_len Size of buffer. Assumed to be size returned by sledge_kv_get_value_size.
|
||||
* @returns 0 on success, 1 if key missing, 2 if buffer too small
|
||||
*/
|
||||
INLINE int
|
||||
scratch_storage_get(uint32_t key_offset, uint32_t key_len, uint32_t buf_offset, uint32_t buf_len)
|
||||
{
|
||||
return sledge_abi__scratch_storage_get(key_offset, key_len, buf_offset, buf_len);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param key_offset
|
||||
* @param key_len
|
||||
* @param value_offset
|
||||
* @param value_len
|
||||
* @returns 0 on success, 1 if already present,
|
||||
*/
|
||||
INLINE int
|
||||
scratch_storage_set(uint32_t key_offset, uint32_t key_len, uint32_t value_offset, uint32_t value_len)
|
||||
{
|
||||
return sledge_abi__scratch_storage_set(key_offset, key_len, value_offset, value_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key_offset
|
||||
* @param key_len
|
||||
* @returns 0 on success, 1 if not present
|
||||
*/
|
||||
INLINE int
|
||||
scratch_storage_delete(uint32_t key_offset, uint32_t key_len)
|
||||
{
|
||||
return sledge_abi__scratch_storage_delete(key_offset, key_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key_offset
|
||||
* @param key_len
|
||||
* @param value_offset
|
||||
* @param value_len
|
||||
* @returns 0 on success, 1 if already present,
|
||||
*/
|
||||
INLINE void
|
||||
scratch_storage_upsert(uint32_t key_offset, uint32_t key_len, uint32_t value_offset, uint32_t value_len)
|
||||
{
|
||||
sledge_abi__scratch_storage_upsert(key_offset, key_len, value_offset, value_len);
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lock.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
/* Simple K-V store based on The Practice of Programming by Kernighan and Pike */
|
||||
|
||||
/* Bucket count is sized to be a prime that is approximately 20% larger than the desired capacity (6k keys) */
|
||||
#define MAP_BUCKET_COUNT 7907
|
||||
#define MAP_HASH jenkins_hash
|
||||
|
||||
struct map_node {
|
||||
struct map_node *next;
|
||||
uint8_t *key;
|
||||
uint8_t *value;
|
||||
uint32_t key_len;
|
||||
uint32_t value_len;
|
||||
uint32_t hash;
|
||||
};
|
||||
|
||||
struct map_bucket {
|
||||
lock_t lock;
|
||||
struct map_node *head;
|
||||
};
|
||||
|
||||
struct map {
|
||||
struct map_bucket buckets[MAP_BUCKET_COUNT];
|
||||
};
|
||||
|
||||
static inline void
|
||||
map_init(struct map *restrict map)
|
||||
{
|
||||
for (int i = 0; i < MAP_BUCKET_COUNT; i++) {
|
||||
map->buckets[i].head = NULL;
|
||||
LOCK_INIT(&map->buckets[i].lock);
|
||||
}
|
||||
};
|
||||
|
||||
/* See https://en.wikipedia.org/wiki/Jenkins_hash_function */
|
||||
static inline uint32_t
|
||||
jenkins_hash(uint8_t *key, uint32_t key_len)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
uint32_t hash = 0;
|
||||
while (i != key_len) {
|
||||
hash += key[i++];
|
||||
hash += hash << 10;
|
||||
hash ^= hash >> 6;
|
||||
}
|
||||
hash += hash << 3;
|
||||
hash ^= hash >> 11;
|
||||
hash += hash << 15;
|
||||
return hash;
|
||||
}
|
||||
|
||||
static inline uint8_t *
|
||||
map_get(struct map *map, uint8_t *key, uint32_t key_len, uint32_t *ret_value_len)
|
||||
{
|
||||
uint8_t *value = NULL;
|
||||
|
||||
uint32_t hash = MAP_HASH(key, key_len);
|
||||
|
||||
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
|
||||
|
||||
LOCK_LOCK(&bucket->lock);
|
||||
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
|
||||
if (node->hash == hash) {
|
||||
value = node->value;
|
||||
*ret_value_len = node->value_len;
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (value == NULL) *ret_value_len = 0;
|
||||
|
||||
DONE:
|
||||
LOCK_UNLOCK(&bucket->lock);
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
map_set(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_t value_len)
|
||||
{
|
||||
bool did_set = false;
|
||||
|
||||
uint32_t hash = MAP_HASH(key, key_len);
|
||||
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
|
||||
LOCK_LOCK(&bucket->lock);
|
||||
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
|
||||
if (node->hash == hash) goto DONE;
|
||||
}
|
||||
|
||||
struct map_node *new_node = (struct map_node *)xmalloc(sizeof(struct map_node));
|
||||
*(new_node) = (struct map_node){ .hash = hash,
|
||||
.key = xmalloc(key_len),
|
||||
.key_len = key_len,
|
||||
.value = xmalloc(value_len),
|
||||
.value_len = value_len,
|
||||
.next = bucket->head };
|
||||
|
||||
// Copy Key and Value
|
||||
memcpy(new_node->key, key, key_len);
|
||||
memcpy(new_node->value, value, value_len);
|
||||
|
||||
bucket->head = new_node;
|
||||
did_set = true;
|
||||
|
||||
DONE:
|
||||
LOCK_UNLOCK(&bucket->lock);
|
||||
return did_set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns boolean if node was deleted or not
|
||||
*/
|
||||
static inline bool
|
||||
map_delete(struct map *map, uint8_t *key, uint32_t key_len)
|
||||
{
|
||||
bool did_delete = false;
|
||||
|
||||
uint32_t hash = MAP_HASH(key, key_len);
|
||||
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
|
||||
LOCK_LOCK(&bucket->lock);
|
||||
|
||||
struct map_node *prev = bucket->head;
|
||||
if (prev != NULL && prev->hash == hash) {
|
||||
bucket->head = prev->next;
|
||||
free(prev->key);
|
||||
free(prev->value);
|
||||
free(prev);
|
||||
did_delete = true;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
for (struct map_node *node = prev->next; node != NULL; prev = node, node = node->next) {
|
||||
prev->next = node->next;
|
||||
free(node->key);
|
||||
free(node->value);
|
||||
free(node);
|
||||
did_delete = true;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
DONE:
|
||||
LOCK_UNLOCK(&bucket->lock);
|
||||
return did_delete;
|
||||
}
|
||||
|
||||
static inline void
|
||||
map_upsert(struct map *map, uint8_t *key, uint32_t key_len, uint8_t *value, uint32_t value_len)
|
||||
{
|
||||
uint32_t hash = MAP_HASH(key, key_len);
|
||||
struct map_bucket *bucket = &map->buckets[hash % MAP_BUCKET_COUNT];
|
||||
LOCK_LOCK(&bucket->lock);
|
||||
|
||||
for (struct map_node *node = bucket->head; node != NULL; node = node->next) {
|
||||
if (node->hash == hash) {
|
||||
node->value_len = value_len;
|
||||
node->value = realloc(node->value, value_len);
|
||||
assert(node->value);
|
||||
memcpy(node->value, value, value_len);
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
|
||||
struct map_node *new_node = (struct map_node *)xmalloc(sizeof(struct map_node));
|
||||
|
||||
*(new_node) = (struct map_node){ .hash = hash,
|
||||
.key = xmalloc(key_len),
|
||||
.key_len = key_len,
|
||||
.value = xmalloc(value_len),
|
||||
.value_len = value_len,
|
||||
.next = bucket->head };
|
||||
|
||||
assert(new_node->key);
|
||||
assert(new_node->value);
|
||||
|
||||
// Copy Key and Value
|
||||
memcpy(new_node->key, key, key_len);
|
||||
memcpy(new_node->value, value, value_len);
|
||||
|
||||
bucket->head = new_node;
|
||||
|
||||
DONE:
|
||||
LOCK_UNLOCK(&bucket->lock);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "likely.h"
|
||||
#include "panic.h"
|
||||
|
||||
static inline void *
|
||||
xmalloc(size_t size)
|
||||
{
|
||||
void *allocation = malloc(size);
|
||||
if (unlikely(allocation == NULL)) panic("xmalloc failed!\n");
|
||||
return allocation;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
res
|
||||
perf.data
|
||||
perf.data.old
|
@ -0,0 +1,46 @@
|
||||
RUNTIME_DIR=../../runtime/
|
||||
SLEDGE_BINARY_DIR=${RUNTIME_DIR}/bin
|
||||
SLEDGE_TESTS_DIR=${RUNTIME_DIR}/tests
|
||||
HOSTNAME=localhost
|
||||
|
||||
all: run
|
||||
|
||||
clean:
|
||||
make -C ${RUNTIME_DIR} clean
|
||||
make -C ${SLEDGE_TESTS_DIR} clean
|
||||
rm -f ${SLEDGE_BINARY_DIR}/html.wasm.so
|
||||
|
||||
${SLEDGE_BINARY_DIR}/sledgert:
|
||||
make -C ${RUNTIME_DIR} runtime
|
||||
|
||||
.PHONY: sledgert
|
||||
sledgert: ${SLEDGE_BINARY_DIR}/sledgert
|
||||
|
||||
${SLEDGE_BINARY_DIR}/scratch_storage.wasm.so:
|
||||
make -C ../../applications scratch_storage.install
|
||||
|
||||
.PHONY: scratch_storage_get
|
||||
scratch_storage_get: ${SLEDGE_BINARY_DIR}/scratch_storage_get.wasm.so
|
||||
|
||||
.PHONY: scratch_storage_set
|
||||
scratch_storage_set: ${SLEDGE_BINARY_DIR}/scratch_storage_set.wasm.so
|
||||
|
||||
run: sledgert scratch_storage_get scratch_storage_set
|
||||
LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} ${SLEDGE_BINARY_DIR}/sledgert spec.json
|
||||
|
||||
debug: sledgert scratch_storage_get scratch_storage_set
|
||||
SLEDGE_DISABLE_PREEMPTION=true SLEDGE_NWORKERS=1 \
|
||||
LD_LIBRARY_PATH=${SLEDGE_BINARY_DIR} gdb ${SLEDGE_BINARY_DIR}/sledgert \
|
||||
--eval-command="handle SIGUSR1 noprint nostop" \
|
||||
--eval-command="handle SIGPIPE noprint nostop" \
|
||||
--eval-command="set pagination off" \
|
||||
--eval-command="run spec.json"
|
||||
|
||||
client-upsert-multi:
|
||||
hey -z 6s -cpus 4 -c 100 -t 0 -o csv -m GET "http://${HOSTNAME}:1337/upsert?test&wakka2"
|
||||
|
||||
client-get-multi:
|
||||
hey -z 6s -cpus 4 -c 100 -t 0 -o csv -m GET "http://${HOSTNAME}:1337/get?test"
|
||||
|
||||
client:
|
||||
http :1337/scratch_storage
|
@ -0,0 +1,2 @@
|
||||
SLEDGE_SCHEDULER=EDF
|
||||
SLEDGE_DISABLE_PREEMPTION=true
|
@ -0,0 +1,3 @@
|
||||
SLEDGE_SCHEDULER=EDF
|
||||
SLEDGE_DISABLE_PREEMPTION=false
|
||||
SLEDGE_SIGALRM_HANDLER=TRIAGED
|
@ -0,0 +1,2 @@
|
||||
SLEDGE_SCHEDULER=FIFO
|
||||
SLEDGE_DISABLE_PREEMPTION=true
|
@ -0,0 +1,2 @@
|
||||
SLEDGE_SCHEDULER=FIFO
|
||||
SLEDGE_DISABLE_PREEMPTION=false
|
@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! command -v hey > /dev/null; then
|
||||
HEY_URL=https://hey-release.s3.us-east-2.amazonaws.com/hey_linux_amd64
|
||||
wget $HEY_URL -O hey
|
||||
chmod +x hey
|
||||
|
||||
if [[ $(whoami) == "root" ]]; then
|
||||
mv hey /usr/bin/hey
|
||||
else
|
||||
sudo mv hey /usr/bin/hey
|
||||
fi
|
||||
fi
|
@ -0,0 +1,44 @@
|
||||
[
|
||||
{
|
||||
"name": "gwu",
|
||||
"port": 1337,
|
||||
"routes": [
|
||||
{
|
||||
"route": "/set",
|
||||
"path": "scratch_storage_set.wasm.so",
|
||||
"expected-execution-us": 10000000,
|
||||
"admissions-percentile": 70,
|
||||
"relative-deadline-us": 20000000,
|
||||
"http-resp-content-type": "text/plain",
|
||||
"http-resp-size": 1024
|
||||
},
|
||||
{
|
||||
"route": "/get",
|
||||
"path": "scratch_storage_get.wasm.so",
|
||||
"expected-execution-us": 10000000,
|
||||
"admissions-percentile": 70,
|
||||
"relative-deadline-us": 20000000,
|
||||
"http-resp-content-type": "text/plain",
|
||||
"http-resp-size": 1024
|
||||
},
|
||||
{
|
||||
"route": "/delete",
|
||||
"path": "scratch_storage_delete.wasm.so",
|
||||
"expected-execution-us": 10000000,
|
||||
"admissions-percentile": 70,
|
||||
"relative-deadline-us": 20000000,
|
||||
"http-resp-content-type": "text/plain",
|
||||
"http-resp-size": 1024
|
||||
},
|
||||
{
|
||||
"route": "/upsert",
|
||||
"path": "scratch_storage_upsert.wasm.so",
|
||||
"expected-execution-us": 10000000,
|
||||
"admissions-percentile": 70,
|
||||
"relative-deadline-us": 20000000,
|
||||
"http-resp-content-type": "text/plain",
|
||||
"http-resp-size": 1024
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Loading…
Reference in new issue