~evanj/cms

d2718d938bf3bdde539593d0cabcb5c4ba863ab0 — Evan M Jones 3 years ago 0244ab6
Fix(cache): Break cache on content under contenttype when contenttype is
updated.
4 files changed, 39 insertions(+), 23 deletions(-)

M TODO
M internal/s/cache/content.go
M internal/s/cache/contenttype.go
M internal/s/db/content.go
M TODO => TODO +0 -11
@@ 1,15 1,4 @@
X Handle update of ReferenceList type.
X Break cache when a Reference or ReferenceList item has been deleted.
Cache listicles.
Break cache on content type update.
Allow updating of space.
Cache: When an item is updated, any item that references it is not.
X What happens when you delete a content's only reference list content? Then try to copy space?
X BUG: Removing field from contenttype seems to be broken.
X BUG: create content type, create content, add string field to content type, copy space broken.
Fullscreen takeover for html/markdown editors.
Sidebar nav for desktop
X Break cache for referrers when content is deleted
X Don't let content reference itself
X On contenttype delete invalid content that refers to content in the contenttype
X Let content be created/updated when a ref is empty.

M internal/s/cache/content.go => internal/s/cache/content.go +2 -2
@@ 58,7 58,7 @@ func (c *Cache) ContentUpdate(space space.Space, ct contenttype.ContentType, ite
	// Remove content from cache that referenced this.
	for _, ref := range list {
		err := c.mc.Delete(fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		if !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
		if err != nil && !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
			return nil, err
		}
	}


@@ 91,7 91,7 @@ func (c *Cache) ContentDelete(space space.Space, ct contenttype.ContentType, ite
	// Remove content from cache that referenced this.
	for _, ref := range list {
		err := c.mc.Delete(fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		if !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
		if err != nil && !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
			return err
		}
	}

M internal/s/cache/contenttype.go => internal/s/cache/contenttype.go +34 -6
@@ 36,16 36,39 @@ func (c *Cache) ContentTypeNew(space space.Space, name string, params []db.Conte
	)
}

func (c *Cache) ContentTypeUpdate(space space.Space, thing contenttype.ContentType, name string, newParams []db.ContentTypeNewParam, updateParams []db.ContentTypeUpdateParam) (contenttype.ContentType, error) {
	thing, err := c.db.ContentTypeUpdate(space, thing, name, newParams, updateParams)
func (c *Cache) ContentTypeUpdate(space space.Space, prev contenttype.ContentType, name string, newParams []db.ContentTypeNewParam, updateParams []db.ContentTypeUpdateParam) (contenttype.ContentType, error) {
	next, err := c.db.ContentTypeUpdate(space, prev, name, newParams, updateParams)
	if err != nil {
		return nil, err
	}

	iter, t, err := c.db.ContentIter(space, next, "name") // TODO: What is this sort type for?
	if err != nil {
		return nil, errors.Wrap(err, "failed to iterate content for cache breaking")
	}
	defer t.Rollback()

	// Remove content from cache that referenced this.
	for iter.Next() {
		prevC, err := iter.Scan()
		if err != nil {
			return nil, err
		}

		err = c.mc.Delete(fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), next.ID(), prevC.ID()))
		if err != nil && !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
			return nil, errors.Wrap(err, "failed to cache break content")
		}
	}

	if err := t.Commit(); err != nil {
		return nil, err
	}

	return c.contenttype(
		true,
		fmt.Sprintf("%s::%s::%s", c.baseKey, space.ID(), thing.ID()),
		func() (contenttype.ContentType, error) { return thing, err },
		fmt.Sprintf("%s::%s::%s", c.baseKey, space.ID(), next.ID()),
		func() (contenttype.ContentType, error) { return next, err },
	)
}



@@ 61,10 84,11 @@ func (c *Cache) ContentTypeDelete(space space.Space, ct contenttype.ContentType)
	key := fmt.Sprintf("%s::%s::%s", c.baseKey, space.ID(), ct.ID())

	// Copy all contents and their values.
	iter, err := c.db.ContentIter(space, ct, "name") // TODO: What is this sort type for?
	iter, t, err := c.db.ContentIter(space, ct, "name") // TODO: What is this sort type for?
	if err != nil {
		return errors.Wrap(err, "failed to iterate referring content for cache breaking")
	}
	defer t.Rollback()

	var list []db.ContentRefSet
	for iter.Next() {


@@ 98,11 122,15 @@ func (c *Cache) ContentTypeDelete(space space.Space, ct contenttype.ContentType)
	// Remove content from cache that referenced this.
	for _, ref := range list {
		err := c.mc.Delete(fmt.Sprintf("%s::%s::%s::%s", c.baseKey, space.ID(), ref.ContentTypeID, ref.ContentID))
		if !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
		if err != nil && !errors.Is(err, memcache.ErrCacheMiss) { // Don't care about cache miss.
			return errors.Wrap(err, "failed to cache break referring content")
		}
	}

	if err := t.Commit(); err != nil {
		return err
	}

	return c.mc.Delete(key)
}


M internal/s/db/content.go => internal/s/db/content.go +3 -4
@@ 1151,13 1151,12 @@ func (db *DB) contentIter(t *sql.Tx, space space.Space, ct contenttype.ContentTy
	return iter
}

func (db *DB) ContentIter(space space.Space, ct contenttype.ContentType, sortField string) (*contentIter, error) {
func (db *DB) ContentIter(space space.Space, ct contenttype.ContentType, sortField string) (*contentIter, *sql.Tx, error) {
	t, err := db.Begin()
	if err != nil {
		return nil, err
		return nil, nil, err
	}
	defer t.Rollback()
	return db.contentIter(t, space, ct, sortField), t.Commit()
	return db.contentIter(t, space, ct, sortField), t, nil
}

func (iter *contentIter) pump() {