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 Edge Server [API] #2202

Merged
merged 27 commits into from
Feb 8, 2025
Merged

Changes to support Edge Server [API] #2202

merged 27 commits into from
Feb 8, 2025

Conversation

snej
Copy link
Collaborator

@snej snej commented Jan 8, 2025

This branch contains all the LiteCore enhancements & fixes needed to implement Edge Server.

IMPORTANT: There are also changes in the EE repo: https://github.com/couchbase/couchbase-lite-core-EE/pull/47

  • Removed the REST server code, and a few support classes like RequestResponse; that's now in Edge Server.
  • Modified RESTSyncListener to avoid using RequestResponse, which simplified the code. (That's in the EE repo.)
  • The replicator now uses the new DatabasePool. By default, DBAccess will create its own pool of size 1, but an alternate Replicator constructor gives an existing shared pool for it to use. This will greatly reduce the overhead of running lots of replicators at once.
  • More stuff, described in the individual commits.

C API Changes:

  • ⚠️ Removed C4ListenerAPIs type
  • ⚠️ Removed apis field from C4ListenerConfig, as well as fields specific to REST APIs, like directory`
  • ⚠️ Removed c4listener_availableAPIs()
  • ⚠️ Removed api parameter from c4listener_getURLs()
  • Added serverName and serverVersion fields to C4ListenerConfig; you can leave them NULL
  • Added c4listener_shareDBWithConfig() and C4ListenerDatabaseConfig struct, for db-specific configuration

C++ API Changes:

  • ⚠️ Removed C4Listener::availableAPIs()
  • ⚠️ Removed api parameter from C4Listener::URLs()
  • Added optional C4ListenerDatabaseConfig const* parameter to C4Listener::shareDB()
  • Added C4Collection::getPurgeCount()
  • Added C4Database::Transaction::isActive()
  • Added a C4DocEnumerator constructor that takes a startKey parameter.
  • Added C4Query::parameterNames()
  • Added [[nodiscard]] and/or LIFETIMEBOUND attributes to some methods. These will intentionally trigger warnings in buggy code; if so, look carefully and fix your code!

@snej
Copy link
Collaborator Author

snej commented Jan 8, 2025

Jenkins builds are failing due to a CMake error:

[2025-01-08T18:54:13.724Z] CMake Error at cmake/platform_linux.cmake:75 (target_link_libraries):
[2025-01-08T18:54:13.724Z]   Target "LiteCoreObjects" links to:
[2025-01-08T18:54:13.724Z] 
[2025-01-08T18:54:13.724Z]     icu::icu4c
[2025-01-08T18:54:13.724Z] 
[2025-01-08T18:54:13.724Z]   but the target was not found.  Possible reasons include:

@snej
Copy link
Collaborator Author

snej commented Jan 8, 2025

Ubuntu in GitHub CI is failing a test due to a runtime exception:

DB Info SQLite message: statement aborts at 36: [CREATE UNIQUE INDEX IF NOT EXISTS "kv_.loopback.test_seqs" ON "kv_.loopback.test" (sequence)] attempt to write a readonly database

I know how to fix this...

@cbl-bot
Copy link

cbl-bot commented Jan 9, 2025

Code Coverage Results:

Type Percentage
branches 66.12
functions 78.12
instantiations 70.24
lines 77.4
regions 73.23

@snej
Copy link
Collaborator Author

snej commented Jan 9, 2025

Jenkins failures:

macOS: The code coverage script push_coverage_results.py blew up somehow. No idea why.

Windows: Test "Continuous Super-Fast Push" failed, due to a DatabasePool deadlock:

[2025-01-09T01:48:20.321Z] Default ERROR LiteCore throwing LiteCore error 16: Timed out waiting on DatabasePool::borrow
[2025-01-09T01:48:20.321Z] 	at litecore::DatabasePool::borrow in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\LiteCore\Support\DatabasePool.cc: line: 183: address: 0x7FF6B4B7EB50
[2025-01-09T01:48:20.321Z] 	at litecore::DatabasePool::borrow in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\LiteCore\Support\DatabasePool.cc: line: 187: address: 0x7FF6B4B7E7E0
[2025-01-09T01:48:20.321Z] 	at litecore::repl::DBAccess::useCollection in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\Replicator\DBAccess.hh: line: 52: address: 0x7FF6B4ABA7A0
[2025-01-09T01:48:20.321Z] 	at litecore::repl::ChangesFeed::shouldPushRev in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\Replicator\ChangesFeed.cc: line: 220: address: 0x7FF6B4AB42B0
[2025-01-09T01:48:20.321Z] 	at litecore::repl::ChangesFeed::shouldPushRev in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\Replicator\ChangesFeed.cc: line: 209: address: 0x7FF6B4AB3190
[2025-01-09T01:48:20.321Z] 	at litecore::repl::Pusher::doneWithRev in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\Replicator\Pusher+Revs.cc: line: 379: address: 0x7FF6B4B30950
[2025-01-09T01:48:20.321Z] 	at litecore::repl::Pusher::sendRevision in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\Replicator\Pusher+Revs.cc: line: 179: address: 0x7FF6B4B2DE70
[2025-01-09T01:48:20.321Z] 	at litecore::repl::Pusher::maybeSendMoreRevs in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\Replicator\Pusher+Revs.cc: line: 34: address: 0x7FF6B4B2DD50
[2025-01-09T01:48:20.321Z] 	at litecore::repl::Pusher::handleChangesResponse in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\Replicator\Pusher.cc: line: 329: address: 0x7FF6B4B167B0
[2025-01-09T01:48:20.321Z] 	at `litecore::repl::Pusher::sendChanges'::`2'::<lambda_1>::operator() in C:\Jenkins\workspace\line_couchbase-lite-core_PR-2202\couchbase-lite-core\Replicator\Pusher.cc: line: 251: address: 

@borrrden
Copy link
Member

borrrden commented Jan 9, 2025

Windows tests appear to be failing now, and linting is needed

@snej snej force-pushed the feature/edge-server branch from 55b1a07 to 2b5a36b Compare January 10, 2025 00:09
@snej snej force-pushed the feature/edge-server branch from ad46321 to 6bd2587 Compare January 22, 2025 22:37
@snej snej changed the title Changes to support Edge Server Changes to support Edge Server [API] Jan 22, 2025
@snej snej force-pushed the feature/edge-server branch from 6bd2587 to 219a0c1 Compare January 22, 2025 23:06
logInfo("initial database is %s", nameOf(main).c_str());
_dbTag = _c4db_getDatabaseTag(main);
Cache& cache = writeable() ? _readWrite : _readOnly;
cache.entries[0].db = std::move(main);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean by moving a pointer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a no-op -- I should take out the move call. I think it's left over from when it used to be a Retained.

struct Entry {
Retained<C4Database> db; ///< Database (may be nullptr)
unsigned borrowCount = 0; ///< Number of borrows
std::thread::id borrower; ///< Thread that borrowed it, if any
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each thread has one entry that may have multiple borrowers, right? And concurrency occurs among the entries, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup.

if ( message[len - 1] == '\n' ) --len;
TLSLogDomain.log(kLogLevels[level], "mbedTLS(%s): %.*s", (role == Client ? "C" : "S"), int(len), message);
// mbedTLS logging callback:
static const LogLevel kLogLevels[] = {LogLevel::Info, LogLevel::Info, LogLevel::Verbose, LogLevel::Verbose,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw at some other place this kLogLevels. Can it be moved to a header?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite the name this isn't anything like the others. It maps mbedTLS log levels to LiteCore levels.

/// Returns the name a database is registered under.
/// `db` need not be the exact instance that was registered;
/// any instance on the same database file will work. */
std::optional<std::string> nameOfDatabase(C4Database*) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can a db have not a name? Assuming db is NONNULL

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the DB's own name, it's the name it's registered as, i.e. the first path component of a URL. If the database isn't being shared, it doesn't have a name.

static_cast<uint64_t>(c4changes[0].sequence),
static_cast<uint64_t>(c4changes[nChanges - 1].sequence));
_maxSequence = c4changes[nChanges - 1].sequence;
continue; // ignore changes I made myself
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intentional changes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! That's a good catch. I thought for a while I had to get rid of that optimization because it wouldn't work with the database pool; but actually it does. I'll put that code back in.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually there was a reason behind that, as I rediscovered when I put it back. That code block is an optimization to ignore "echoes": changes made by a corresponding puller. However it had been nonfunctional for years (the 'external' flag would never be false because it's observing a C4Database that's never used for writes.) With the DatabasePool changes, that code now works, but (a) it exposes a bug somewhere in the pusher that doesn't update the checkpoint, thus the test failure; and (b) it's not going to work correctly in Edge Server where everything is sharing that writeable db.

tl;dr: I took it out again and it fixed the test. I left an explanatory comment in its place.

snej added 17 commits February 4, 2025 15:27
Returns true if it created or replaced the index, false if it already exists
Just wrapped a try/catch around the entire method so it properly
returns the error in `outError` instead of throwing (which would
terminate since this is a noexcept method.)
Used by Edge Server, to support the startKey and endKey parameters
to `/db/_all_docs`.
Needed by Edge Server so the `/db/_query` handler can validate that
the caller provided values for all parameters, and only those params.

Also added LIFETIMEBOUND to some methods that return unowned refs.
When called on a background thread they were not logging info about
the error.
Changed it from an Assert() to throwMbedTLSError().
- Fixed a bug in existing `split()` when the string ends with the
  delimiter.
- Added new `split()` without a callback, returning a vector.
- Added `split2()` that returns a pair.
- Added `trimWhitespace`.
SecKeyCreateSignature has a tendency to throw C++ exceptions down
inside Apple's code, that are caught and handled therein; this was
causing Xcode to break on those 'throws'.
Fixed by adding an ExpectingExceptions object around the call.
so that it can be shared between cbl-logtest (in the tools repo)
and CppTests
HTTP headers must not contain control characters other than tab,
or the delete character.
This also catches incorrect linebreaks, like `\n` or `\r`.
This function was moved here from RequestResponse, which is going away
Using a Writer as a backing store was clever but there were some
nasty bugs when copying a Headers object. Replaced it with a simpler
scheme that just stores a vector of alloc_slices, one per string.
Some fairly normal things would log scary-looking errors that
shouldn't appear in Edge Server logs by default!
@snej snej force-pushed the feature/edge-server branch from 219a0c1 to 4788ebe Compare February 5, 2025 00:59
snej added 5 commits February 4, 2025 17:20
The old method was groping internal state of the TLSContext,
requiring TCPSocket to be a friend of TLSContext.
Now TLSContext has a wrapSocket() method that does the dirty work.
- Better error codes: they should be HTTP codes rather than WebSocket
  (I know it's WebSocketDomain, but it's used for both)
- Handle invalid 'Connection' header value
- Log errors, usually as warnings except for ECONNRESET since it's
  pretty common to get it on the server side.
nothing using it yet
These showed up when using DatabasePool, since it vends read-only
C4Databases:
- C4BlobStore threw a NotFound exception in its constructor if the
  db didn't already have an Attachments dir
- DatabaseImpl::createBlobStore shouldn't try to create the store
  if read-only, even if `force` is set
- When opening a DB, throw if the `create` flag is set but `writeable`
  isn't; otherwise SQLite will return a confusing SQLITE_MISUSE
  error
- Throw NotWriteable when trying to create an ExclusiveTransaction
  on a read-only DataFile
@snej snej force-pushed the feature/edge-server branch from 4788ebe to 79243a9 Compare February 5, 2025 01:21
snej added 5 commits February 5, 2025 10:36
DBAccess now has a DatabasePool instead of using access_lock.
If constructed the normal way, it creates its own pool with capacity
2, so it has one read-only and one writeable database as before.
  It can also be passed an existing DatabasePool, which is what Edge
Server does (it has a bigger pool shared by the entire server.)
There is plumbing all the way through to c4Replicator, to allow the
client to pass in its own DatabasePool.
  The API changed slightly since it now returns BorrowedDatabase or
BorrowedCollection objects.

Lots of replicator objects were using C4Collection* pointers even
when they didn't have a lock on the DBAccess. This seemed pretty
dangerous and didn't play very well with the DatabasePool, so I
changed it so each Worker knows its CollectionSpec and uses that.
The implementation of the REST API has moved to Edge Server.
C4Listener now only supports the 'Sync' API.

- Removed the REST server code, and a few support classes like
  RequestResponse; that's now in Edge Server.
- Modified RESTSyncListener to avoid using the now-missing
  RequestResponse, which simplified the code. (That's in the EE repo.)

C API Changes:
- Removed `C4ListenerAPIs` type
- Removed `apis` field from `C4ListenerConfig, as well as fields
  specific to REST APIs, like `directory`
- Removed `c4listener_availableAPIs()`
- Removed `api` parameter from `c4listener_getURLs()`
- Added `serverName` and `serverVersion` fields to `C4ListenerConfig`;
  you can leave them NULL
- Added `c4listener_shareDBWithConfig()` and `C4ListenerDatabaseConfig`
  struct, for db-specific configuration

C++ API Changes:
- Removed `C4Listener::availableAPIs()`
- Removed `api` parameter from `C4Listener::URLs()`
- Added optional `C4ListenerDatabaseConfig const*` parameter to
  `C4Listener::shareDB()`
The only significant change is "Avoid UBSan error iterating empty
SmallVector", which Edge Server was running into.
@snej snej force-pushed the feature/edge-server branch from 79243a9 to 7391250 Compare February 5, 2025 18:39
@snej snej merged commit 403e27d into master Feb 8, 2025
7 of 8 checks passed
@snej snej deleted the feature/edge-server branch February 8, 2025 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants