Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to support 'liteserv' #2117

Closed
wants to merge 43 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
476f4d4
Replicator's DBAccess now uses a database pool
snej Nov 15, 2024
9876bd4
more db pool in replicator
snej Nov 18, 2024
f1a453a
c4ReplicatorImpl now supports DatabasePool
snej Nov 18, 2024
209d43b
build fixes for CE and Xcode
snej Nov 18, 2024
ec21613
Changes for EdgeServer
snej Aug 20, 2024
b2e3b4b
SyncListener creates Replicator w/DatabasePool
snej Nov 19, 2024
096fd0d
SyncListener uses Task. Ensure Tasks get stopped.
snej Nov 20, 2024
c9e665a
DatabasePool logging
snej Nov 20, 2024
3860ec9
Support v3 protocol in P2P sync
snej Nov 20, 2024
caf75f2
CppTests pass again
snej Nov 20, 2024
515faee
Make WITH_ERROR info more visible in test output
snej Nov 20, 2024
aed2f47
DatabasePool improvements (Transaction)
snej Nov 20, 2024
f9e992a
Request/Response improvements
snej Nov 20, 2024
5be017c
clang-format
snej Nov 20, 2024
8fdee81
C4Listener: Per-database configuration
snej Nov 21, 2024
8c99e38
REST: Refactoring. Moved more code into EdgeServer.
snej Nov 23, 2024
e20beea
DatabasePool: Proper error if pool is read-only
snej Nov 25, 2024
bf00ffb
Internal API to start replicator with DatabasePool
snej Nov 25, 2024
ce9ffb7
Added HTTPListener::Task::listed()
snej Dec 2, 2024
4e2e837
Fixed uninitialized ptr in BorrowedCollection struct
snej Dec 2, 2024
833a63c
Fixed misleading comment in c4ReplicatorTypes.h
snej Dec 3, 2024
95b46fd
C4Collection::createIndex() now returns bool [API]
snej Dec 3, 2024
56b3ffd
Added missing include to HTTPListener.hh
snej Dec 3, 2024
2c08390
Added HTTPListener::findMatchingSyncProtocol()
snej Dec 4, 2024
0cac59d
Fixed warnings, reformatted
snej Dec 4, 2024
ab5daf9
Fixed nasty use-after-free bug in Headers move-assignment
snej Dec 4, 2024
4404356
Fixing Xcode and Windows builds
snej Dec 5, 2024
98f4c0e
Resolved GCC warnings w/CBL_CORE_API
snej Dec 5, 2024
770663d
cleanup
snej Dec 5, 2024
3825962
Simplified implementation of Headers
snej Dec 5, 2024
0ddbf87
TCPSocket: Don't log multiple identical errors
snej Dec 5, 2024
95f6d4a
Avoid GCC multi-line comment warning in c4ReplicatorTypes
snej Dec 5, 2024
f5b4b10
C4Listener: Made stopping more robust
snej Dec 6, 2024
8982590
HTTPListener: use task_id not pid in Task JSON description
snej Dec 9, 2024
4baf0a1
C4DocEnumerator: Added `startKey` option [API]
snej Dec 9, 2024
bf15191
Attempt to fix MSVC link error w/ kC4Cpp_DefaultLog
snej Dec 11, 2024
a668579
Refactor the CMake project
borrrden Dec 11, 2024
2eff78e
Fixed UBSan error calling C4Listener::shareDB()
snej Dec 11, 2024
1018db5
Added C4Collection::getPurgeCount() [API]
snej Dec 20, 2024
0b7a3c2
HTTPListener: use "age_sece" not "started_on" in _active_tasks
snej Dec 20, 2024
9ff9472
Switch to official cbdeps build of icu
borrrden Dec 20, 2024
bcf5f25
CMake: Fixed LITECORE_SANITIZE
snej Dec 21, 2024
72c2ec8
Removed unnecessary/conflicting change in Replicator.cc
snej Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions C/Cpp_include/c4Base.hh
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,6 @@ namespace litecore {
class PublicKey;
} // namespace crypto

namespace REST {
class Listener;
class RESTListener;
} // namespace REST

namespace websocket {
class WebSocket;
}
Expand Down
4 changes: 3 additions & 1 deletion C/Cpp_include/c4Collection.hh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct C4Collection
virtual uint64_t getDocumentCount() const = 0;

virtual C4SequenceNumber getLastSequence() const = 0;
virtual uint64_t getPurgeCount() const = 0;

C4ExtraInfo& extraInfo() noexcept { return _extraInfo; }

Expand Down Expand Up @@ -93,7 +94,8 @@ struct C4Collection
/// Same as the C4Database method, but the query will refer to this collection by default.
Retained<C4Query> newQuery(C4QueryLanguage language, slice queryExpr, int* outErrorPos) const;

virtual void createIndex(slice name, slice indexSpec, C4QueryLanguage indexLanguage, C4IndexType indexType,
/// Returns true if it created or replaced the index, false if it already exists.
virtual bool createIndex(slice name, slice indexSpec, C4QueryLanguage indexLanguage, C4IndexType indexType,
const C4IndexOptions* C4NULLABLE indexOptions = nullptr) = 0;

virtual Retained<C4Index> getIndex(slice name) = 0;
Expand Down
10 changes: 6 additions & 4 deletions C/Cpp_include/c4Database.hh
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ struct C4Database
/** Attempts to discover and verify the named extension in the provided path */
static void enableExtension(slice name, slice path);

static bool exists(slice name, slice inDirectory);
static void copyNamed(slice sourcePath, slice destinationName, const Config&);
static bool deleteNamed(slice name, slice inDirectory);
static bool deleteAtPath(slice path);
static bool exists(slice name, slice inDirectory);
static void copyNamed(slice sourcePath, slice destinationName, const Config&);
[[nodiscard]] static bool deleteNamed(slice name, slice inDirectory);
[[nodiscard]] static bool deleteAtPath(slice path);

static Retained<C4Database> openNamed(slice name, const Config&);

Expand Down Expand Up @@ -184,6 +184,8 @@ struct C4Database
db->endTransaction(false);
}

bool isActive() const noexcept { return _db != nullptr; }

~Transaction() {
if ( _db ) _db->endTransaction(false);
}
Expand Down
7 changes: 7 additions & 0 deletions C/Cpp_include/c4DocEnumerator.hh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ struct C4DocEnumerator
explicit C4DocEnumerator(C4Collection* collection,
const C4EnumeratorOptions& options = kC4DefaultEnumeratorOptions);

/// Creates an enumerator on a collection, beginning at `startKey`.
/// (This means that if the order is descending, `startKey` will be the maximum key.)
/// If `startKey` is null, it's ignored and all documents are returned.
/// You must first call \ref next to step to the first document.
explicit C4DocEnumerator(C4Collection* collection, slice startKey,
const C4EnumeratorOptions& options = kC4DefaultEnumeratorOptions);

/// Creates an enumerator on a collection, ordered by sequence.
/// You must first call \ref next to step to the first document.
explicit C4DocEnumerator(C4Collection* collection, C4SequenceNumber since,
Expand Down
74 changes: 55 additions & 19 deletions C/Cpp_include/c4Listener.hh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#pragma once
#include "c4Base.hh"
#include "c4ListenerTypes.h"
#include "fleece/FLBase.h"
#include "fleece/InstanceCounted.hh"
#include <vector>

Expand All @@ -24,41 +25,76 @@ C4_ASSUME_NONNULL_BEGIN
// the dynamic library only exports the C API.
// ************************************************************************

namespace litecore::REST {
class HTTPListener;
}

/** A lightweight server that shares databases over the network for replication.
@note This class is not ref-counted. Instances must be explicitly deleted/destructed. */
struct C4Listener final
: public fleece::InstanceCounted
, C4Base {
static C4ListenerAPIs availableAPIs();

explicit C4Listener(C4ListenerConfig config);

~C4Listener() override;

bool shareDB(slice name, C4Database* db);

/// Constructor. Starts the listener (asynchronously) but does not share any databases.
explicit C4Listener(C4ListenerConfig const& config);

~C4Listener() noexcept override;

/// Stops the listener. If you don't call this, the destructor will do it for you.
C4Error stop() noexcept;

/// Shares a database, and its default collection.
/// @param name The URI name (first path component) in the HTTP API.
/// If `nullslice`, the C4Database's name will be used (possibly URL-escaped).
/// The name may not include '/', '.', control characters, or non-ASCII characters.
/// @param db The database to share. On success this instance is now managed by the Listener
/// and should not be used again by the caller.
/// @param dbConfig Optional configuration for this database. Overrides the C4ListenerConfig.
/// @returns True on success, false if the name is already in use.
[[nodiscard]] bool shareDB(slice name, C4Database* db,
C4ListenerDatabaseConfig const* C4NULLABLE dbConfig = nullptr);

/// Stops sharing a database. `db` need not be the exact instance that was registered;
/// any instance on the same database file will work.
bool unshareDB(C4Database* db);

bool shareCollection(slice name, C4Collection* coll);

bool unshareCollection(slice name, C4Collection* coll);

/// Adds a collection to be shared.
/// @note A database's default collection is automatically shared.
/// @param name The URI name the database is registered by.
/// @param collection The collection instance to share.
/// @returns True on success, false if `name` is not registered. */
[[nodiscard]] bool shareCollection(slice name, C4Collection* collection);

/// Stops sharing a collection.
/// @note Call this after \ref registerDatabase if you don't want to share the default collection.
/// @param name The URI name the database is registered by.
/// @param collection The collection instance.
/// @returns True on success, false if the database name or collection is not registered. */
bool unshareCollection(slice name, C4Collection* collection);

/// The TCP port number for incoming connections.
[[nodiscard]] uint16_t port() const;

/// Returns first the number of connections, and second the number of active connections.
[[nodiscard]] std::pair<unsigned, unsigned> connectionStatus() const;

std::vector<std::string> URLs(C4Database* C4NULLABLE db, C4ListenerAPIs api) const;
/// Returns the URL(s) of a database being shared, or of the root.
/// The URLs will differ only in their hostname -- there will be one for each IP address or known
/// hostname of the computer, or of the network interface.
[[nodiscard]] std::vector<std::string> URLs(C4Database* C4NULLABLE db) const;

static std::string URLNameFromPath(slice path);
/// A convenience that, given a filesystem path to a database, returns the database name
/// for use in an HTTP URI path.
[[nodiscard]] static std::string URLNameFromPath(slice path);

C4Listener(const C4Listener&) = delete;

// internal use only
C4Listener(C4ListenerConfig const& config, Retained<litecore::REST::HTTPListener> impl);

private:
// For some reason, MSVC on Jenkins will not compile this with noexcept (everything else will)
C4Listener(C4Listener&&); // NOLINT(performance-noexcept-move-constructor)
C4Listener(C4Listener&&) noexcept;

Retained<litecore::REST::RESTListener> _impl;
C4ListenerHTTPAuthCallback C4NULLABLE _httpAuthCallback;
void* C4NULLABLE _callbackContext;
Retained<litecore::REST::HTTPListener> _impl;
};

C4_ASSUME_NONNULL_END
12 changes: 7 additions & 5 deletions C/Cpp_include/c4Query.hh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <utility>

C4_ASSUME_NONNULL_BEGIN
Expand Down Expand Up @@ -45,11 +46,12 @@ struct C4Query final
int* C4NULLABLE outErrorPos);

unsigned columnCount() const noexcept;
slice columnTitle(unsigned col) const;
slice columnTitle(unsigned col) const LIFETIMEBOUND;
alloc_slice explain() const;

alloc_slice parameters() const noexcept;
void setParameters(slice parameters);
const std::set<std::string>& parameterNames() const noexcept LIFETIMEBOUND;
alloc_slice parameters() const noexcept;
void setParameters(slice parameters);

alloc_slice fullTextMatched(const C4FullTextMatch&);

Expand All @@ -62,8 +64,8 @@ struct C4Query final
[[nodiscard]] int64_t rowCount() const;
void seek(int64_t rowIndex);

[[nodiscard]] FLArrayIterator columns() const;
[[nodiscard]] FLValue column(unsigned i) const;
[[nodiscard]] FLArrayIterator columns() const LIFETIMEBOUND;
[[nodiscard]] FLValue column(unsigned i) const LIFETIMEBOUND;

[[nodiscard]] unsigned fullTextMatchCount() const;
[[nodiscard]] C4FullTextMatch fullTextMatch(unsigned i) const;
Expand Down
14 changes: 7 additions & 7 deletions C/c4Base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ using namespace litecore;


extern "C" {
CBL_CORE_API std::atomic_int gC4ExpectExceptions;
bool C4ExpectingExceptions();
std::atomic_int gC4ExpectExceptions;
bool C4ExpectingExceptions();

bool C4ExpectingExceptions() { return gC4ExpectExceptions > 0; } // LCOV_EXCL_LINE
}
Expand Down Expand Up @@ -174,11 +174,11 @@ C4StringResult c4log_binaryFilePath(void) C4API {
}

// NOLINTBEGIN(misc-misplaced-const,cppcoreguidelines-interfaces-global-init)
CBL_CORE_API C4LogDomain const kC4DefaultLog = (C4LogDomain)&kC4Cpp_DefaultLog;
CBL_CORE_API C4LogDomain const kC4DatabaseLog = (C4LogDomain)&DBLog;
CBL_CORE_API C4LogDomain const kC4QueryLog = (C4LogDomain)&QueryLog;
CBL_CORE_API C4LogDomain const kC4SyncLog = (C4LogDomain)&SyncLog;
CBL_CORE_API C4LogDomain const kC4WebSocketLog = (C4LogDomain)&websocket::WSLogDomain;
C4LogDomain const kC4DefaultLog = (C4LogDomain)&kC4Cpp_DefaultLog;
C4LogDomain const kC4DatabaseLog = (C4LogDomain)&DBLog;
C4LogDomain const kC4QueryLog = (C4LogDomain)&QueryLog;
C4LogDomain const kC4SyncLog = (C4LogDomain)&SyncLog;
C4LogDomain const kC4WebSocketLog = (C4LogDomain)&websocket::WSLogDomain;

// NOLINTEND(misc-misplaced-const,cppcoreguidelines-interfaces-global-init)

Expand Down
3 changes: 1 addition & 2 deletions C/c4BlobStore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ C4BlobStore::C4BlobStore(slice dirPath, C4DatabaseFlags flags, const C4Encryptio
FilePath dir(_dirPath, "");
if ( dir.exists() ) {
dir.mustExistAsDir();
} else {
if ( !(flags & kC4DB_Create) ) error::_throw(error::NotFound);
} else if ( !(flags & kC4DB_ReadOnly) ) {
dir.mkdir();
}
}
Expand Down
2 changes: 1 addition & 1 deletion C/c4Certificate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ using namespace litecore::crypto;

#ifdef COUCHBASE_ENTERPRISE

CBL_CORE_API const C4CertIssuerParameters kDefaultCertIssuerParameters = {
const C4CertIssuerParameters kDefaultCertIssuerParameters = {
CertSigningRequest::kOneYear, C4STR("1"), -1, false, true, true, true};


Expand Down
4 changes: 2 additions & 2 deletions C/c4Database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ using namespace fleece;
using namespace litecore;


CBL_CORE_API const char* const kC4DatabaseFilenameExtension = ".cblite2";
const char* const kC4DatabaseFilenameExtension = ".cblite2";

CBL_CORE_API C4StorageEngine const kC4SQLiteStorageEngine = "SQLite";
C4StorageEngine const kC4SQLiteStorageEngine = "SQLite";

C4EncryptionKey C4EncryptionKeyFromPassword(slice password, C4EncryptionAlgorithm alg) {
C4EncryptionKey key;
Expand Down
64 changes: 35 additions & 29 deletions C/c4DocEnumerator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,36 @@ using namespace litecore;

#pragma mark - DOC ENUMERATION:

CBL_CORE_API const C4EnumeratorOptions kC4DefaultEnumeratorOptions = {kC4IncludeNonConflicted | kC4IncludeBodies};
const C4EnumeratorOptions kC4DefaultEnumeratorOptions = {kC4IncludeNonConflicted | kC4IncludeBodies};

static RecordEnumerator::Options recordOptions(const C4EnumeratorOptions& c4options, slice startKey) {
RecordEnumerator::Options options;
if ( c4options.flags & kC4Descending ) options.sortOption = kDescending;
else if ( c4options.flags & kC4Unsorted )
options.sortOption = kUnsorted;
options.includeDeleted = (c4options.flags & kC4IncludeDeleted) != 0;
options.onlyConflicts = (c4options.flags & kC4IncludeNonConflicted) == 0;
if ( (c4options.flags & kC4IncludeBodies) == 0 ) options.contentOption = kMetaOnly;
else
options.contentOption = kEntireBody;
options.startKey = startKey;
return options;
}

static RecordEnumerator::Options recordOptions(const C4EnumeratorOptions& c4options, C4SequenceNumber since) {
auto options = recordOptions(c4options, nullslice);
options.minSequence = since + 1;
return options;
}

class C4DocEnumerator::Impl
: public RecordEnumerator
, public fleece::InstanceCounted {
, public InstanceCounted {
public:
Impl(C4Collection* collection, sequence_t since, const C4EnumeratorOptions& options)
: RecordEnumerator(asInternal(collection)->keyStore(), since, recordOptions(options))
Impl(C4Collection* collection, const C4EnumeratorOptions& c4Options, const RecordEnumerator::Options& options)
: RecordEnumerator(asInternal(collection)->keyStore(), options)
, _collection(asInternal(collection))
, _options(options) {}

Impl(C4Collection* collection, const C4EnumeratorOptions& options)
: RecordEnumerator(asInternal(collection)->keyStore(), recordOptions(options))
, _collection(asInternal(collection))
, _options(options) {}

static RecordEnumerator::Options recordOptions(const C4EnumeratorOptions& c4options) {
RecordEnumerator::Options options;
if ( c4options.flags & kC4Descending ) options.sortOption = kDescending;
else if ( c4options.flags & kC4Unsorted )
options.sortOption = kUnsorted;
options.includeDeleted = (c4options.flags & kC4IncludeDeleted) != 0;
options.onlyConflicts = (c4options.flags & kC4IncludeNonConflicted) == 0;
if ( (c4options.flags & kC4IncludeBodies) == 0 ) options.contentOption = kMetaOnly;
else
options.contentOption = kEntireBody;
return options;
}
, _c4Options(c4Options) {}

Retained<C4Document> getDoc() {
if ( !hasRecord() ) return nullptr;
Expand All @@ -62,7 +64,8 @@ class C4DocEnumerator::Impl
if ( !this->hasRecord() ) return false;

revid vers(record().version());
if ( (_options.flags & kC4IncludeRevHistory) && vers.isVersion() ) _docRevID = vers.asVersionVector().asASCII();
if ( (_c4Options.flags & kC4IncludeRevHistory) && vers.isVersion() )
_docRevID = vers.asVersionVector().asASCII();
else
_docRevID = vers.expanded();

Expand All @@ -78,15 +81,18 @@ class C4DocEnumerator::Impl

private:
litecore::CollectionImpl* _collection;
C4EnumeratorOptions const _options;
C4EnumeratorOptions const _c4Options;
alloc_slice _docRevID;
};

C4DocEnumerator::C4DocEnumerator(C4Collection* collection, C4SequenceNumber since, const C4EnumeratorOptions& options)
: _impl(new Impl(collection, since, options)) {}

C4DocEnumerator::C4DocEnumerator(C4Collection* collection, const C4EnumeratorOptions& options)
: _impl(new Impl(collection, options)) {}
: C4DocEnumerator(collection, nullslice, options) {}

C4DocEnumerator::C4DocEnumerator(C4Collection* collection, slice startKey, const C4EnumeratorOptions& options)
: _impl(new Impl(collection, options, recordOptions(options, startKey))) {}

C4DocEnumerator::C4DocEnumerator(C4Collection* collection, C4SequenceNumber since, const C4EnumeratorOptions& options)
: _impl(new Impl(collection, options, recordOptions(options, since))) {}

#ifndef C4_STRICT_COLLECTION_API
C4DocEnumerator::C4DocEnumerator(C4Database* database, const C4EnumeratorOptions& options)
Expand Down
Loading
Loading