Skip to content

Cut down on redundant database queries#112

Open
laffer1 wants to merge 3 commits intomainfrom
feature/tuning_upgrade
Open

Cut down on redundant database queries#112
laffer1 wants to merge 3 commits intomainfrom
feature/tuning_upgrade

Conversation

@laffer1
Copy link
Copy Markdown
Member

@laffer1 laffer1 commented Jan 23, 2026

Add a performance optimization for mport_upgrade to cut down on redundant sqlite queries.

Summary by Sourcery

Introduce caching of package index checks and moved package lookups in the upgrade path to reduce redundant database queries and bump the library version.

Enhancements:

  • Add in-memory caches for mport_index_check results and moved package lookups during mport_upgrade to avoid repeated sqlite queries across packages and dependencies.
  • Extend mport_update_down to reuse cached index check results throughout recursive dependency upgrades.

Build:

  • Bump the internal mport library version from 2.7.5 to 2.7.6.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Jan 23, 2026

Reviewer's Guide

Adds hash-based caching for mport upgrade operations on MidnightBSD to avoid repeating expensive SQLite index and moved-lookup queries, threads the cache through recursive update calls, and bumps the library version.

Sequence diagram for cached index_check and moved_lookup in mport_upgrade

sequenceDiagram
    participant Caller
    participant mport_upgrade
    participant moved_cache
    participant index_cache
    participant mport_moved_lookup
    participant mport_index_check

    Caller->>mport_upgrade: call mport_upgrade(mport)
    activate mport_upgrade

    mport_upgrade->>mport_upgrade: ohash_init(h)
    mport_upgrade->>mport_upgrade: ohash_init(moved_cache)
    mport_upgrade->>mport_upgrade: ohash_init(index_cache)

    loop for each pack in packs
        mport_upgrade->>moved_cache: ohash_qlookup(origin)
        mport_upgrade->>moved_cache: ohash_find(slot)
        alt moved lookup cached
            moved_cache-->>mport_upgrade: return cached entries
        else not cached
            mport_upgrade->>mport_moved_lookup: mport_moved_lookup(origin, entries)
            alt movedEntries is NULL or empty
                mport_upgrade->>moved_cache: ecalloc cache_entry
                mport_upgrade->>moved_cache: ohash_insert(negative)
                mport_upgrade->>mport_upgrade: continue
            else entries present
                mport_upgrade->>moved_cache: ecalloc cache_entry
                mport_upgrade->>moved_cache: ohash_insert(positive)
            end
        end

        alt package needs update or origin match
            mport_upgrade->>index_cache: ohash_qlookup(name)
            mport_upgrade->>index_cache: ohash_find(slot)
            alt index_check cached
                index_cache-->>mport_upgrade: return cached result
            else not cached
                mport_upgrade->>mport_index_check: mport_index_check(pack)
                mport_index_check-->>mport_upgrade: match
                mport_upgrade->>index_cache: ecalloc cache_entry
                mport_upgrade->>index_cache: ohash_insert(result)
            end
        end
    end

    mport_upgrade->>mport_upgrade: free caches with efree
    mport_upgrade->>mport_upgrade: ohash_delete(moved_cache)
    mport_upgrade->>mport_upgrade: ohash_delete(index_cache)
    deactivate mport_upgrade
    mport_upgrade-->>Caller: return updated
Loading

Sequence diagram for recursive mport_update_down with shared index_cache

sequenceDiagram
    participant mport_upgrade
    participant mport_update_down
    participant index_cache
    participant mport_index_check

    mport_upgrade->>mport_update_down: mport_update_down(mport, pack, info, h, index_cache)
    activate mport_update_down

    loop for each dependency pack in depends
        mport_update_down->>mport_update_down: check h for pack
        alt not yet processed
            mport_update_down->>mport_update_down: recurse
            mport_update_down->>mport_update_down: mport_update_down(mport, depends, info, h, index_cache)

            mport_update_down->>index_cache: ohash_qlookup(depends->name)
            mport_update_down->>index_cache: ohash_find(slot)
            alt cached result exists
                index_cache-->>mport_update_down: return cached result
            else no cached result
                mport_update_down->>mport_index_check: mport_index_check(depends)
                mport_index_check-->>mport_update_down: match
                alt index_cache not NULL
                    mport_update_down->>index_cache: ecalloc cache_entry
                    mport_update_down->>index_cache: ohash_insert(result)
                end
            end

            alt match is true
                mport_update_down->>mport_update_down: perform update
                mport_update_down->>mport_update_down: insert name into h
            end
        end
    end

    mport_update_down->>index_cache: ohash_qlookup(pack->name)
    mport_update_down->>index_cache: ohash_find(slot)
    alt cached result exists
        index_cache-->>mport_update_down: return cached result
    else no cached result
        mport_update_down->>mport_index_check: mport_index_check(pack)
        mport_index_check-->>mport_update_down: match
        alt index_cache not NULL
            mport_update_down->>index_cache: ecalloc cache_entry
            mport_update_down->>index_cache: ohash_insert(result)
        end
    end

    alt match is true
        mport_update_down->>mport_update_down: update pack and insert name into h
    end

    deactivate mport_update_down
    mport_update_down-->>mport_upgrade: ret
Loading

Class diagram for new cache structs and updated functions

classDiagram
    class mportInstance

    class mportPackageMeta {
        char* name
        char* origin
        int action
        bool automatic
    }

    class mportIndexMovedEntry {
        char date
        char* moved_to_pkgname
    }

    class mportIndexEntry {
        char* pkgname
    }

    class index_check_cache_t {
        int result
        bool cached
    }

    class moved_lookup_cache_t {
        mportIndexMovedEntry** entries
        bool cached
    }

    class ohash_info {
        int unused
        void* data
    }

    class ohash {
        unsigned int size
    }

    class mport_upgrade {
        +int mport_upgrade(mportInstance* mport)
    }

    class mport_update_down {
        +int mport_update_down(mportInstance* mport, mportPackageMeta* pack, ohash_info* info, ohash* h, ohash* index_cache)
    }

    class mport_moved_lookup {
        +int mport_moved_lookup(mportInstance* mport, char* origin, mportIndexMovedEntry*** movedEntries)
    }

    class mport_index_check {
        +int mport_index_check(mportInstance* mport, mportPackageMeta* pack)
    }

    class memory_helpers {
        +void* ecalloc(size_t size, void* data)
        +void efree(void* p, size_t s1, void* data)
    }

    mport_upgrade --> mportInstance
    mport_upgrade --> mportPackageMeta
    mport_upgrade --> ohash_info
    mport_upgrade --> ohash : h
    mport_upgrade --> ohash : moved_cache
    mport_upgrade --> ohash : index_cache
    mport_upgrade --> mport_moved_lookup
    mport_upgrade --> mport_index_check
    mport_upgrade --> mport_update_down
    mport_upgrade --> memory_helpers

    mport_update_down --> mportInstance
    mport_update_down --> mportPackageMeta
    mport_update_down --> ohash_info
    mport_update_down --> ohash : h
    mport_update_down --> ohash : index_cache
    mport_update_down --> mport_index_check
    mport_update_down --> memory_helpers

    moved_lookup_cache_t --> mportIndexMovedEntry
    index_check_cache_t ..> mport_index_check
    moved_lookup_cache_t ..> mport_moved_lookup

    ohash ..> index_check_cache_t
    ohash ..> moved_lookup_cache_t
Loading

Flow diagram for cache lifecycle in mport_upgrade

flowchart TD
    A[start mport_upgrade] --> B[ohash_init h]
    B --> C[ohash_init moved_cache]
    C --> D[ohash_init index_cache]

    D --> E[iterate packs]
    E --> F{pack already in h?}
    F -->|yes| E
    F -->|no| G[moved_cache lookup by origin]

    G --> H{moved lookup cached?}
    H -->|yes| I[use cached movedEntries]
    H -->|no| J[mport_moved_lookup]

    J --> K{entries found?}
    K -->|no| L[insert negative moved_lookup_cache_t
for origin] --> E
    K -->|yes| M[insert positive moved_lookup_cache_t
with entries] --> N

    I --> N{movedEntries indicate move or expiry?}
    N -->|yes| O[handle move or expire
set actions and continue] --> E
    N -->|no| P[index_cache lookup by name]

    P --> Q{index_check cached?}
    Q -->|yes| R[use cached result]
    Q -->|no| S[mport_index_check
store result in index_check_cache_t]

    R --> T{result requires update?}
    S --> T

    T -->|no| E
    T -->|yes| U[call mport_update_down
with shared index_cache]
    U --> E

    E --> V{no more packs?}
    V -->|no| E
    V -->|yes| W[free moved_cache entries
with efree]
    W --> X[ohash_delete moved_cache]
    X --> Y[free index_cache entries
with efree]
    Y --> Z[ohash_delete index_cache]
    Z --> AA[ohash_delete h]
    AA --> AB[end mport_upgrade]
Loading

File-Level Changes

Change Details Files
Introduce hash-based caches to avoid repeated mport_moved_lookup calls during upgrade processing.
  • Define moved_lookup_cache_t to hold cached mportIndexMovedEntry results and a cached flag.
  • Initialize a new ohash (moved_cache) keyed by package origin for moved lookup results in mport_upgrade.
  • On first moved lookup per origin, call mport_moved_lookup and store either NULL (negative) or the returned entries (positive) in moved_cache; on subsequent iterations reuse cached results instead of querying SQLite again.
  • Simplify moved_to_pkgname check to only test for non-empty string, assuming pointer is non-NULL.
  • At function teardown, iterate over moved_cache to free cache structs (but not the underlying moved entries) and delete the hash.
libmport/upgrade.c
Introduce caching for mport_index_check results and thread the cache through recursive dependency updates.
  • Define index_check_cache_t to hold the integer result of mport_index_check with a cached flag.
  • Initialize an ohash (index_cache) keyed by package name in mport_upgrade and pass it into mport_update_down on MidnightBSD builds.
  • In mport_upgrade, before calling mport_index_check, look up the package name in index_cache and reuse the stored result when present; otherwise call mport_index_check once and insert the result into the cache.
  • Extend mport_update_down signature and implementation to accept an index_cache hash and use it whenever it needs to call mport_index_check for the main package and its dependencies, inserting new entries as needed.
  • Ensure updated and dependency packages are marked in the processed-package hash (h) using ohash_qlookup before ohash_insert, matching the new call sites.
  • At function teardown, iterate over index_cache to free cache entry structs and delete the hash.
libmport/upgrade.c
libmport/mport_private.h
Bump internal library version to reflect the performance optimization release.
  • Update MPORT_VERSION macro from 2.7.5 to 2.7.6.
libmport/mport_private.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • The index/moved cache lookup and insert logic is duplicated in several places in mport_upgrade and mport_update_down; consider extracting small helper functions (e.g., index_cache_get_or_fill(...), moved_cache_get_or_fill(...)) to make the flow easier to read and reduce the chance of inconsistencies on future changes.
  • Both cache structs carry a cached boolean even though the presence of an entry in the ohash already distinguishes cached vs. non-cached; you could simplify the code by treating a found entry as cached and encoding negative results via the stored value (e.g., entries == NULL or result == 0) instead of maintaining a separate flag.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The index/moved cache lookup and insert logic is duplicated in several places in `mport_upgrade` and `mport_update_down`; consider extracting small helper functions (e.g., `index_cache_get_or_fill(...)`, `moved_cache_get_or_fill(...)`) to make the flow easier to read and reduce the chance of inconsistencies on future changes.
- Both cache structs carry a `cached` boolean even though the presence of an entry in the `ohash` already distinguishes cached vs. non-cached; you could simplify the code by treating a found entry as cached and encoding negative results via the stored value (e.g., `entries == NULL` or `result == 0`) instead of maintaining a separate flag.

## Individual Comments

### Comment 1
<location> `libmport/upgrade.c:171` </location>
<code_context>
 		}		

-		if ((*movedEntries)->moved_to_pkgname != NULL && (*movedEntries)->moved_to_pkgname[0]!= '\0') {   
+		if ((*movedEntries)->moved_to_pkgname[0] != '\0') {   
 			mport_call_msg_cb(mport, "Package %s has moved to %s. Migrating %s\n", (*packs)->name, (*movedEntries)->moved_to_pkgname,  (*movedEntries)->moved_to_pkgname);
 			(*packs)->action = MPORT_ACTION_UPGRADE;
</code_context>

<issue_to_address>
**issue (bug_risk):** Potential NULL dereference on moved_to_pkgname after removing the NULL check.

The original code safely handled a possible NULL `moved_to_pkgname` via `(*movedEntries)->moved_to_pkgname != NULL && ...`. The new version dereferences it directly with `(*movedEntries)->moved_to_pkgname[0]`, which will crash if it’s NULL. Unless there is now a strong guarantee this pointer is always non-NULL, please restore the NULL check or enforce non-NULL earlier in the flow.
</issue_to_address>

### Comment 2
<location> `libmport/upgrade.c:126-129` </location>
<code_context>
 			continue;
 		}
+
+		/* Check cache for moved lookup */
+		slot = ohash_qlookup(&moved_cache, (*packs)->origin);
+		moved_lookup_cache_t *cached_moved = (moved_lookup_cache_t *)ohash_find(&moved_cache, slot);
+		if (cached_moved != NULL && cached_moved->cached) {
+			movedEntries = cached_moved->entries;
+			use_cached_moved = true;
</code_context>

<issue_to_address>
**issue (bug_risk):** Cached negative moved-lookup results are not handled consistently, leading to NULL dereferences.

For cached negative results, `cached_moved->cached` is true but `cached_moved->entries` is NULL. This branch then sets `movedEntries` and `use_cached_moved = true` and skips `mport_moved_lookup`, but later code assumes `movedEntries`/`*movedEntries` are non-NULL and dereferences them, causing a crash. You could either `continue` when `cached_moved->entries == NULL` (matching the non-cached negative path) or explicitly represent a negative cache entry and branch before dereferencing.
</issue_to_address>

### Comment 3
<location> `libmport/upgrade.c:64` </location>
<code_context>
 	free(p);
 }

+/* Cache structure for index check results */
+typedef struct {
+	int result;  /* 0 = no update, 1 = update available, 2 = origin match */
</code_context>

<issue_to_address>
**issue (complexity):** Consider simplifying the new caching code by removing the explicit cached flags and extracting common cache lookup/update/cleanup logic into small helper functions to reduce duplication and conditionals.

You can keep the caching feature but significantly reduce complexity and repetition with a couple of small helpers and by simplifying the cache structs.

### 1. Drop the `cached` flag and use `NULL` as the sentinel

Right now every call does:

```c
if (cached != NULL && cached->cached) {
    ...
}
```

You can simplify the structs and logic so that presence in the hash means “cached”:

```c
typedef struct {
    int result;  /* 0 = no update, 1 = update available, 2 = origin match */
} index_check_cache_t;

typedef struct {
    mportIndexMovedEntry **entries;  /* NULL means "no moved entries" */
} moved_lookup_cache_t;
```

Then lookup becomes:

```c
slot = ohash_qlookup(&index_cache, (*packs)->name);
index_check_cache_t *cached = ohash_find(&index_cache, slot);
if (cached != NULL) {
    match = cached->result;
    use_cached_index = true;
}
```

This removes the extra boolean and the `&& cached->cached` checks everywhere.

### 2. Factor out the repeated index-check caching pattern

The `index_cache` lookup/fill pattern is repeated in `mport_upgrade` and `mport_update_down`. A small helper keeps the upgrade logic clean and encapsulates all `#if` and `ohash` details:

```c
#if defined(__MidnightBSD__)
static int
cached_index_check(mportInstance *mport,
                   mportPackageMeta *pack,
                   struct ohash *index_cache)
{
    unsigned int slot;
    index_check_cache_t *cached;

    if (index_cache != NULL) {
        slot = ohash_qlookup(index_cache, pack->name);
        cached = ohash_find(index_cache, slot);
        if (cached != NULL) {
            return cached->result;
        }
    }

    int match = mport_index_check(mport, pack);

    if (index_cache != NULL) {
        index_check_cache_t *entry = ecalloc(sizeof(*entry), NULL);
        entry->result = match;
        ohash_insert(index_cache, slot, entry);
    }

    return match;
}
#else
static int
cached_index_check(mportInstance *mport,
                   mportPackageMeta *pack,
                   void *unused)
{
    (void)unused;
    return mport_index_check(mport, pack);
}
#endif
```

Then all call sites become:

```c
int match = cached_index_check(mport, pack, index_cache);
```

and

```c
int match = cached_index_check(mport, *depends, index_cache);
```

This collapses the repeated `match/use_cached/#if` blocks into one place and makes the signature change to `mport_update_down` easier to justify.

### 3. Factor out moved-lookup caching

Similarly, the moved lookup caching block in `mport_upgrade` can be moved to a helper:

```c
#if defined(__MidnightBSD__)
static mportIndexMovedEntry **
cached_moved_lookup(mportInstance *mport,
                    const char *origin,
                    struct ohash *moved_cache)
{
    unsigned int slot;
    moved_lookup_cache_t *cached;
    mportIndexMovedEntry **entries = NULL;

    if (moved_cache != NULL) {
        slot = ohash_qlookup(moved_cache, origin);
        cached = ohash_find(moved_cache, slot);
        if (cached != NULL) {
            return cached->entries;  /* may be NULL for negative cache */
        }
    }

    if (mport_moved_lookup(mport, origin, &entries) != MPORT_OK) {
        return NULL;
    }

    if (moved_cache != NULL) {
        moved_lookup_cache_t *entry = ecalloc(sizeof(*entry), NULL);
        entry->entries = entries; /* may be NULL: negative result */
        ohash_insert(moved_cache, slot, entry);
    }

    return entries;
}
#else
static mportIndexMovedEntry **
cached_moved_lookup(mportInstance *mport,
                    const char *origin,
                    void *unused)
{
    (void)unused;
    mportIndexMovedEntry **entries = NULL;
    if (mport_moved_lookup(mport, origin, &entries) != MPORT_OK)
        return NULL;
    return entries;
}
#endif
```

Then the loop in `mport_upgrade` becomes:

```c
mportIndexMovedEntry **movedEntries =
    cached_moved_lookup(mport, (*packs)->origin, &moved_cache);

if (movedEntries == NULL || *movedEntries == NULL) {
    packs++;
    continue;
}
```

This removes the inline `use_cached_moved` logic and cache-entry allocation from the main control flow.

### 4. Factor out cache cleanup

The cleanup loops for both caches are almost identical. A small helper reduces boilerplate and keeps `mport_upgrade` focused on the high-level logic:

```c
#if defined(__MidnightBSD__)
static void
free_index_cache(struct ohash *cache)
{
    unsigned int slot;
    const char *key = NULL;

    for (slot = ohash_first(cache, &key);
         key != NULL;
         slot = ohash_next(cache, &slot)) {
        index_check_cache_t *entry = ohash_find(cache, slot);
        if (entry != NULL) {
            efree(entry, sizeof(*entry), NULL);
        }
    }
    ohash_delete(cache);
}

static void
free_moved_cache(struct ohash *cache)
{
    unsigned int slot;
    const char *key = NULL;

    for (slot = ohash_first(cache, &key);
         key != NULL;
         slot = ohash_next(cache, &slot)) {
        moved_lookup_cache_t *entry = ohash_find(cache, slot);
        if (entry != NULL) {
            efree(entry, sizeof(*entry), NULL);
        }
    }
    ohash_delete(cache);
}
#endif
```

Then:

```c
#if defined(__MidnightBSD__)
free_moved_cache(&moved_cache);
free_index_cache(&index_cache);
ohash_delete(&h);
#endif
```

This removes two near-identical loops from `mport_upgrade`.

---

These changes preserve the new caching behavior, but:

- remove the redundant `cached` flag,
- centralize the cache logic and `#if` noise in a few small helpers,
- reduce repeated patterns in `mport_upgrade` and `mport_update_down`,
- and simplify the function bodies back towards their original shape.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread libmport/upgrade.c Outdated
Comment thread libmport/upgrade.c
Comment on lines +126 to +129
/* Check cache for moved lookup */
slot = ohash_qlookup(&moved_cache, (*packs)->origin);
moved_lookup_cache_t *cached_moved = (moved_lookup_cache_t *)ohash_find(&moved_cache, slot);
if (cached_moved != NULL && cached_moved->cached) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Cached negative moved-lookup results are not handled consistently, leading to NULL dereferences.

For cached negative results, cached_moved->cached is true but cached_moved->entries is NULL. This branch then sets movedEntries and use_cached_moved = true and skips mport_moved_lookup, but later code assumes movedEntries/*movedEntries are non-NULL and dereferences them, causing a crash. You could either continue when cached_moved->entries == NULL (matching the non-cached negative path) or explicitly represent a negative cache entry and branch before dereferencing.

Comment thread libmport/upgrade.c
free(p);
}

/* Cache structure for index check results */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (complexity): Consider simplifying the new caching code by removing the explicit cached flags and extracting common cache lookup/update/cleanup logic into small helper functions to reduce duplication and conditionals.

You can keep the caching feature but significantly reduce complexity and repetition with a couple of small helpers and by simplifying the cache structs.

1. Drop the cached flag and use NULL as the sentinel

Right now every call does:

if (cached != NULL && cached->cached) {
    ...
}

You can simplify the structs and logic so that presence in the hash means “cached”:

typedef struct {
    int result;  /* 0 = no update, 1 = update available, 2 = origin match */
} index_check_cache_t;

typedef struct {
    mportIndexMovedEntry **entries;  /* NULL means "no moved entries" */
} moved_lookup_cache_t;

Then lookup becomes:

slot = ohash_qlookup(&index_cache, (*packs)->name);
index_check_cache_t *cached = ohash_find(&index_cache, slot);
if (cached != NULL) {
    match = cached->result;
    use_cached_index = true;
}

This removes the extra boolean and the && cached->cached checks everywhere.

2. Factor out the repeated index-check caching pattern

The index_cache lookup/fill pattern is repeated in mport_upgrade and mport_update_down. A small helper keeps the upgrade logic clean and encapsulates all #if and ohash details:

#if defined(__MidnightBSD__)
static int
cached_index_check(mportInstance *mport,
                   mportPackageMeta *pack,
                   struct ohash *index_cache)
{
    unsigned int slot;
    index_check_cache_t *cached;

    if (index_cache != NULL) {
        slot = ohash_qlookup(index_cache, pack->name);
        cached = ohash_find(index_cache, slot);
        if (cached != NULL) {
            return cached->result;
        }
    }

    int match = mport_index_check(mport, pack);

    if (index_cache != NULL) {
        index_check_cache_t *entry = ecalloc(sizeof(*entry), NULL);
        entry->result = match;
        ohash_insert(index_cache, slot, entry);
    }

    return match;
}
#else
static int
cached_index_check(mportInstance *mport,
                   mportPackageMeta *pack,
                   void *unused)
{
    (void)unused;
    return mport_index_check(mport, pack);
}
#endif

Then all call sites become:

int match = cached_index_check(mport, pack, index_cache);

and

int match = cached_index_check(mport, *depends, index_cache);

This collapses the repeated match/use_cached/#if blocks into one place and makes the signature change to mport_update_down easier to justify.

3. Factor out moved-lookup caching

Similarly, the moved lookup caching block in mport_upgrade can be moved to a helper:

#if defined(__MidnightBSD__)
static mportIndexMovedEntry **
cached_moved_lookup(mportInstance *mport,
                    const char *origin,
                    struct ohash *moved_cache)
{
    unsigned int slot;
    moved_lookup_cache_t *cached;
    mportIndexMovedEntry **entries = NULL;

    if (moved_cache != NULL) {
        slot = ohash_qlookup(moved_cache, origin);
        cached = ohash_find(moved_cache, slot);
        if (cached != NULL) {
            return cached->entries;  /* may be NULL for negative cache */
        }
    }

    if (mport_moved_lookup(mport, origin, &entries) != MPORT_OK) {
        return NULL;
    }

    if (moved_cache != NULL) {
        moved_lookup_cache_t *entry = ecalloc(sizeof(*entry), NULL);
        entry->entries = entries; /* may be NULL: negative result */
        ohash_insert(moved_cache, slot, entry);
    }

    return entries;
}
#else
static mportIndexMovedEntry **
cached_moved_lookup(mportInstance *mport,
                    const char *origin,
                    void *unused)
{
    (void)unused;
    mportIndexMovedEntry **entries = NULL;
    if (mport_moved_lookup(mport, origin, &entries) != MPORT_OK)
        return NULL;
    return entries;
}
#endif

Then the loop in mport_upgrade becomes:

mportIndexMovedEntry **movedEntries =
    cached_moved_lookup(mport, (*packs)->origin, &moved_cache);

if (movedEntries == NULL || *movedEntries == NULL) {
    packs++;
    continue;
}

This removes the inline use_cached_moved logic and cache-entry allocation from the main control flow.

4. Factor out cache cleanup

The cleanup loops for both caches are almost identical. A small helper reduces boilerplate and keeps mport_upgrade focused on the high-level logic:

#if defined(__MidnightBSD__)
static void
free_index_cache(struct ohash *cache)
{
    unsigned int slot;
    const char *key = NULL;

    for (slot = ohash_first(cache, &key);
         key != NULL;
         slot = ohash_next(cache, &slot)) {
        index_check_cache_t *entry = ohash_find(cache, slot);
        if (entry != NULL) {
            efree(entry, sizeof(*entry), NULL);
        }
    }
    ohash_delete(cache);
}

static void
free_moved_cache(struct ohash *cache)
{
    unsigned int slot;
    const char *key = NULL;

    for (slot = ohash_first(cache, &key);
         key != NULL;
         slot = ohash_next(cache, &slot)) {
        moved_lookup_cache_t *entry = ohash_find(cache, slot);
        if (entry != NULL) {
            efree(entry, sizeof(*entry), NULL);
        }
    }
    ohash_delete(cache);
}
#endif

Then:

#if defined(__MidnightBSD__)
free_moved_cache(&moved_cache);
free_index_cache(&index_cache);
ohash_delete(&h);
#endif

This removes two near-identical loops from mport_upgrade.


These changes preserve the new caching behavior, but:

  • remove the redundant cached flag,
  • centralize the cache logic and #if noise in a few small helpers,
  • reduce repeated patterns in mport_upgrade and mport_update_down,
  • and simplify the function bodies back towards their original shape.

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.

1 participant