text:
package org.apache.hyracks.storage.common.buffercache;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.api.io.FileReference;
import org.apache.hyracks.api.io.IFileHandle;
import org.apache.hyracks.api.io.IIOManager;
import org.apache.hyracks.api.lifecycle.ILifeCycleComponent;
import org.apache.hyracks.api.replication.IIOReplicationManager;
import org.apache.hyracks.api.util.IoUtil;
import org.apache.hyracks.storage.common.file.BufferedFileHandle;
import org.apache.hyracks.storage.common.file.IFileMapManager;

public class BufferCache implements IBufferCacheInternal, ILifeCycleComponent {

    private static final Logger LOGGER = Logger.getLogger(BufferCache.class.getName());
    private static final int MAP_FACTOR = 3;

    private static final int MIN_CLEANED_COUNT_DIFF = 3;
    private static final int PIN_MAX_WAIT_TIME = 50;
    private static final int PIN_ATTEMPT_CYCLES_WARNING_THRESHOLD = 3;
    private static final int MAX_PIN_ATTEMPT_CYCLES = 1000;
    public static final boolean DEBUG = false;

    private final int pageSize;
    private final int maxOpenFiles;
    final IIOManager ioManager;
    private final CacheBucket[] pageMap;
    private final IPageReplacementStrategy pageReplacementStrategy;
    private final IPageCleanerPolicy pageCleanerPolicy;
    private final IFileMapManager fileMapManager;
    private final CleanerThread cleanerThread;
    private final Map<Integer, BufferedFileHandle> fileInfoMap;
    private final AsyncFIFOPageQueueManager fifoWriter;
    private final Queue<BufferCacheHeaderHelper> headerPageCache = new ConcurrentLinkedQueue<>();

    //DEBUG
    private Level fileOpsLevel = Level.FINE;
    private ArrayList<CachedPage> confiscatedPages;
    private Lock confiscateLock;
    private HashMap<CachedPage, StackTraceElement[]> confiscatedPagesOwner;
    private ConcurrentHashMap<CachedPage, StackTraceElement[]> pinnedPageOwner;
    //!DEBUG
    private IIOReplicationManager ioReplicationManager;
    private final List<ICachedPageInternal> cachedPages = new ArrayList<>();
    private final AtomicLong masterPinCount = new AtomicLong();

    private boolean closed;

    public BufferCache(IIOManager ioManager, IPageReplacementStrategy pageReplacementStrategy,
            IPageCleanerPolicy pageCleanerPolicy, IFileMapManager fileMapManager, int maxOpenFiles,
            ThreadFactory threadFactory) {
        this.ioManager = ioManager;
        this.pageSize = pageReplacementStrategy.getPageSize();
        this.maxOpenFiles = maxOpenFiles;
        pageReplacementStrategy.setBufferCache(this);
        pageMap = new CacheBucket[pageReplacementStrategy.getMaxAllowedNumPages() * MAP_FACTOR + 1];
        for (int i = 0; i < pageMap.length; ++i) {
            pageMap[i] = new CacheBucket();
        }
        this.pageReplacementStrategy = pageReplacementStrategy;
        this.pageCleanerPolicy = pageCleanerPolicy;
        this.fileMapManager = fileMapManager;

        Executor executor = Executors.newCachedThreadPool(threadFactory);
        fileInfoMap = new HashMap<>();
        cleanerThread = new CleanerThread();
        executor.execute(cleanerThread);
        closed = false;

        fifoWriter = new AsyncFIFOPageQueueManager(this);
        if (DEBUG) {
            confiscatedPages = new ArrayList<>();
            confiscatedPagesOwner = new HashMap<>();
            confiscateLock = new ReentrantLock();
            pinnedPageOwner = new ConcurrentHashMap<>();
        }
    }

    //this constructor is used when replication is enabled to pass the IIOReplicationManager
    public BufferCache(IIOManager ioManager, IPageReplacementStrategy pageReplacementStrategy,
            IPageCleanerPolicy pageCleanerPolicy, IFileMapManager fileMapManager, int maxOpenFiles,
            ThreadFactory threadFactory, IIOReplicationManager ioReplicationManager) {

        this(ioManager, pageReplacementStrategy, pageCleanerPolicy, fileMapManager, maxOpenFiles, threadFactory);
        this.ioReplicationManager = ioReplicationManager;
    }

    @Override
    public int getPageSize() {
        return pageSize;
    }

    @Override
    public int getPageSizeWithHeader() {
        return pageSize + RESERVED_HEADER_BYTES;
    }

    @Override
    public int getNumPages() {
        return pageReplacementStrategy.getMaxAllowedNumPages();
    }

    private void pinSanityCheck(long dpid) throws HyracksDataException {
        if (closed) {
            throw new HyracksDataException("pin called on a closed cache");
        }

        // check whether file has been created and opened
        int fileId = BufferedFileHandle.getFileId(dpid);
        BufferedFileHandle fInfo;
        synchronized (fileInfoMap) {
            fInfo = fileInfoMap.get(fileId);
        }
        if (fInfo == null) {
            throw new HyracksDataException("pin called on a fileId " + fileId + " that has not been created.");
        } else if (fInfo.getReferenceCount() <= 0) {
            throw new HyracksDataException("pin called on a fileId " + fileId + " that has not been opened.");
        }
    }

    @Override
    public ICachedPage tryPin(long dpid) throws HyracksDataException {
        // Calling the pinSanityCheck should be used only for debugging, since
        // the synchronized block over the fileInfoMap is a hot spot.
        if (DEBUG) {
            pinSanityCheck(dpid);
        }
        CachedPage cPage = null;
        int hash = hash(dpid);
        CacheBucket bucket = pageMap[hash];
        bucket.bucketLock.lock();
        try {
            cPage = bucket.cachedPage;
            while (cPage != null) {
                if (cPage.dpid == dpid) {
                    cPage.pinCount.incrementAndGet();
                    pageReplacementStrategy.notifyCachePageAccess(cPage);
                    return cPage;
                }
                cPage = cPage.next;
            }
        } finally {
            bucket.bucketLock.unlock();
        }
        return cPage;
    }

    @Override
    public ICachedPage pin(long dpid, boolean newPage) throws HyracksDataException {
        // Calling the pinSanityCheck should be used only for debugging, since
        // the synchronized block over the fileInfoMap is a hot spot.
        if (DEBUG) {
            pinSanityCheck(dpid);
        }
        CachedPage cPage = findPage(dpid);
        if (!newPage) {
            if (DEBUG) {
                confiscateLock.lock();
                try {
                    for (CachedPage c : confiscatedPages) {
                        if (c.dpid == dpid && c.confiscated.get()) {
                            throw new IllegalStateException();
                        }
                    }
                } finally {
                    confiscateLock.unlock();
                }
            }
            // Resolve race of multiple threads trying to read the page from
            // disk.
            synchronized (cPage) {
                if (!cPage.valid) {
                    read(cPage);
                    cPage.valid = true;
                }
            }
        } else {
            cPage.valid = true;
        }
        pageReplacementStrategy.notifyCachePageAccess(cPage);
        if (DEBUG) {
            pinnedPageOwner.put(cPage, Thread.currentThread().getStackTrace());
        }
        return cPage;
    }

    private CachedPage findPage(long dpid) throws HyracksDataException {
        return (CachedPage) getPageLoop(dpid, -1, false);
    }

    private ICachedPage findPageInner(long dpid) {
        CachedPage cPage;
        /*
         * Hash dpid to get a bucket and then check if the page exists in
         * the bucket.
         */
        int hash = hash(dpid);
        CacheBucket bucket = pageMap[hash];
        bucket.bucketLock.lock();
        try {
            cPage = bucket.cachedPage;
            while (cPage != null) {
                if (DEBUG) {
                    assert bucket.cachedPage != bucket.cachedPage.next;
                }
                if (cPage.dpid == dpid) {
                    if (DEBUG) {
                        assert !cPage.confiscated.get();
                    }
                    cPage.pinCount.incrementAndGet();
                    return cPage;
                }
                cPage = cPage.next;
            }
        } finally {
            bucket.bucketLock.unlock();
        }
        /*
         * If we got here, the page was not in the hash table. Now we ask
         * the page replacement strategy to find us a victim.
         */
        CachedPage victim = (CachedPage) pageReplacementStrategy.findVictim();
        if (victim == null) {
            return null;
        }
        /*
         * We have a victim with the following invariants. 1. The dpid
         * on the CachedPage may or may not be valid. 2. We have a pin
         * on the CachedPage. We have to deal with three cases here.
         * Case 1: The dpid on the CachedPage is invalid (-1). This
         * indicates that this buffer has never been used or is a
         * confiscated page. So we are the only ones holding it. Get a lock
         * on the required dpid's hash bucket, check if someone inserted
         * the page we want into the table. If so, decrement the
         * pincount on the victim and return the winner page in the
         * table. If such a winner does not exist, insert the victim and
         * return it. Case 2: The dpid on the CachedPage is valid. Case
         * 2a: The current dpid and required dpid hash to the same
         * bucket. Get the bucket lock, check that the victim is still
         * at pinCount == 1 If so check if there is a winning CachedPage
         * with the required dpid. If so, decrement the pinCount on the
         * victim and return the winner. If not, update the contents of
         * the CachedPage to hold the required dpid and return it. If
         * the picCount on the victim was != 1 or CachedPage was dirty
         * someone used the victim for its old contents -- Decrement the
         * pinCount and retry. Case 2b: The current dpid and required
         * dpid hash to different buckets. Get the two bucket locks in
         * the order of the bucket indexes (Ordering prevents
         * deadlocks). Check for the existence of a winner in the new
         * bucket and for potential use of the victim (pinCount != 1).
         * If everything looks good, remove the CachedPage from the old
         * bucket, and add it to the new bucket and update its header
         * with the new dpid.
         */
        if (victim.dpid < 0) {
            /*
             * Case 1.
             */
            bucket.bucketLock.lock();
            try {
                if (!victim.pinCount.compareAndSet(0, 1)) {
                    return null;
                }
                // now that we have the pin, ensure the victim's dpid still is < 0, if it's not, decrement
                // pin count and try again
                if (victim.dpid >= 0) {
                    victim.pinCount.decrementAndGet();
                    return null;
                }
                if (DEBUG) {
                    confiscateLock.lock();
                    try {
                        if (confiscatedPages.contains(victim)) {
                            throw new IllegalStateException();
                        }
                    } finally {
                        confiscateLock.unlock();
                    }
                }
                cPage = findTargetInBucket(dpid, bucket.cachedPage, victim);
                if (cPage != null) {
                    return cPage;
                }
                victim.reset(dpid);
                victim.next = bucket.cachedPage;
                bucket.cachedPage = victim;
            } finally {
                bucket.bucketLock.unlock();
            }

            if (DEBUG) {
                assert !victim.confiscated.get();
            }
            return victim;
        }
        int victimHash = hash(victim.dpid);
        if (victimHash == hash) {
            /*
             * Case 2a.
             */
            bucket.bucketLock.lock();
            try {
                if (!victim.pinCount.compareAndSet(0, 1)) {
                    return null;
                }
                // now that we have the pin, ensure the victim's bucket hasn't changed, if it has, decrement
                // pin count and try again
                if (victimHash != hash(victim.dpid)) {
                    victim.pinCount.decrementAndGet();
                    return null;
                }
                if (DEBUG) {
                    confiscateLock.lock();
                    try {
                        if (confiscatedPages.contains(victim)) {
                            throw new IllegalStateException();
                        }
                    } finally {
                        confiscateLock.unlock();
                    }
                }
                cPage = findTargetInBucket(dpid, bucket.cachedPage, victim);
                if (cPage != null) {
                    return cPage;
                }
                victim.reset(dpid);
            } finally {
                bucket.bucketLock.unlock();
            }
            if (DEBUG) {
                assert !victim.confiscated.get();
            }
            return victim;
        } else {
            /*
             * Case 2b.
             */
            CacheBucket victimBucket = pageMap[victimHash];
            if (victimHash < hash) {
                victimBucket.bucketLock.lock();
                bucket.bucketLock.lock();
            } else {
                bucket.bucketLock.lock();
                victimBucket.bucketLock.lock();
            }
            try {
                if (!victim.pinCount.compareAndSet(0, 1)) {
                    return null;
                }
                // now that we have the pin, ensure the victim's bucket hasn't changed, if it has, decrement
                // pin count and try again
                if (victimHash != hash(victim.dpid)) {
                    victim.pinCount.decrementAndGet();
                    return null;
                }
                if (DEBUG && confiscatedPages.contains(victim)) {
                    throw new IllegalStateException();
                }
                cPage = findTargetInBucket(dpid, bucket.cachedPage, victim);
                if (cPage != null) {
                    return cPage;
                }
                if (victimBucket.cachedPage == victim) {
                    victimBucket.cachedPage = victim.next;
                } else {
                    CachedPage victimPrev = victimBucket.cachedPage;
                    while (victimPrev.next != victim) {
                        victimPrev = victimPrev.next;
                        if (victimPrev == null) {
                            throw new IllegalStateException();
                        }
                    }
                    victimPrev.next = victim.next;
                }
                victim.reset(dpid);
                victim.next = bucket.cachedPage;
                bucket.cachedPage = victim;
            } finally {
                victimBucket.bucketLock.unlock();
                bucket.bucketLock.unlock();
            }
            if (DEBUG) {
                assert !victim.confiscated.get();
            }
            return victim;
        }
    }

    private CachedPage findTargetInBucket(long dpid, CachedPage cPage, CachedPage victim) {
        while (cPage != null) {
            if (cPage.dpid == dpid) {
                cPage.pinCount.incrementAndGet();
                victim.pinCount.decrementAndGet();
                if (DEBUG) {
                    assert !cPage.confiscated.get();
                }
                break;
            }
            cPage = cPage.next;
        }
        return cPage;
    }

    private String dumpState() {
        StringBuilder buffer = new StringBuilder();
        buffer.append("Buffer cache state\n");
        buffer.append("Page Size: ").append(pageSize).append('\n');
        buffer.append("Number of physical pages: ").append(pageReplacementStrategy.getMaxAllowedNumPages())
                .append('\n');
        buffer.append("Hash table size: ").append(pageMap.length).append('\n');
        buffer.append("Page Map:\n");
        buffer.append("cpid -> [fileId:pageId, pinCount, valid/invalid, confiscated/physical, dirty/clean]");
        int nCachedPages = 0;
        for (int i = 0; i < pageMap.length; ++i) {
            final CacheBucket cb = pageMap[i];
            cb.bucketLock.lock();
            try {
                CachedPage cp = cb.cachedPage;
                if (cp == null) {
                    continue;
                }
                buffer.append("   ").append(i).append('\n');
                while (cp != null) {
                    buffer.append("      ").append(cp.cpid).append(" -> [")
                            .append(BufferedFileHandle.getFileId(cp.dpid)).append(':')
                            .append(BufferedFileHandle.getPageId(cp.dpid)).append(", ").append(cp.pinCount.get())
                            .append(", ").append(cp.valid ? "valid" : "invalid").append(", ")
                            .append(cp.confiscated.get() ? "confiscated" : "physical").append(", ")
                            .append(cp.dirty.get() ? "dirty" : "clean").append("]\n");
                    cp = cp.next;
                    ++nCachedPages;
                }
            } finally {
                cb.bucketLock.unlock();
            }
        }
        buffer.append("Number of cached pages: ").append(nCachedPages).append('\n');
        if (DEBUG) {
            confiscateLock.lock();
            try {
                buffer.append("Number of confiscated pages: ").append(confiscatedPages.size()).append('\n');
            } finally {
                confiscateLock.unlock();
            }
        }
        return buffer.toString();
    }

    @Override
    public boolean isClean() {
        List<Long> reachableDpids = new LinkedList<>();
        synchronized (cachedPages) {
            for (ICachedPageInternal internalPage : cachedPages) {
                CachedPage c = (CachedPage) internalPage;
                if (c.confiscated() || c.latch.getReadLockCount() != 0 || c.latch.getWriteHoldCount() != 0) {
                    return false;
                }
                if (c.valid) {
                    reachableDpids.add(c.dpid);
                }
            }
        }
        for (Long l : reachableDpids) {
            if (!canFindValidCachedPage(l)) {
                return false;
            }
        }
        return true;
    }

    private boolean canFindValidCachedPage(long dpid) {
        int hash = hash(dpid);
        CachedPage cPage = null;
        CacheBucket bucket = pageMap[hash];
        bucket.bucketLock.lock();
        try {
            cPage = bucket.cachedPage;
            while (cPage != null) {
                assert bucket.cachedPage != bucket.cachedPage.next;
                if (cPage.dpid == dpid) {
                    return true;
                }
                cPage = cPage.next;
            }
        } finally {
            bucket.bucketLock.unlock();
        }
        return false;
    }

    private void read(CachedPage cPage) throws HyracksDataException {
        BufferedFileHandle fInfo = getFileInfo(cPage);
        cPage.buffer.clear();
        BufferCacheHeaderHelper header = checkoutHeaderHelper();
        try {
            long bytesRead = ioManager.syncRead(fInfo.getFileHandle(),
                    getOffsetForPage(BufferedFileHandle.getPageId(cPage.dpid)), header.prepareRead());

            if (bytesRead != getPageSizeWithHeader()) {
                if (bytesRead == -1) {
                    // disk order scan code seems to rely on this behavior, so silently return
                    return;
                }
                throw new HyracksDataException("Failed to read a complete page: " + bytesRead);
            }
            int totalPages = header.processRead(cPage);

            if (totalPages > 1) {
                pageReplacementStrategy.fixupCapacityOnLargeRead(cPage);
                cPage.buffer.position(pageSize);
                cPage.buffer.limit(totalPages * pageSize);
                ioManager.syncRead(fInfo.getFileHandle(), getOffsetForPage(cPage.getExtraBlockPageId()), cPage.buffer);
            }
        } finally {
            returnHeaderHelper(header);
        }
    }

    private long getOffsetForPage(long pageId) {
        return pageId * getPageSizeWithHeader();
    }

    @Override
    public void resizePage(ICachedPage cPage, int totalPages, IExtraPageBlockHelper extraPageBlockHelper)
            throws HyracksDataException {
        pageReplacementStrategy.resizePage((ICachedPageInternal) cPage, totalPages, extraPageBlockHelper);
    }

    BufferedFileHandle getFileInfo(CachedPage cPage) throws HyracksDataException {
        return getFileInfo(BufferedFileHandle.getFileId(cPage.dpid));
    }

    BufferedFileHandle getFileInfo(int fileId) throws HyracksDataException {
        BufferedFileHandle fInfo;
        synchronized (fileInfoMap) {
            fInfo = fileInfoMap.get(fileId);
        }
        if (fInfo == null) {
            throw new HyracksDataException("No such file mapped");
        }
        return fInfo;
    }

    private BufferCacheHeaderHelper checkoutHeaderHelper() {
        BufferCacheHeaderHelper helper = headerPageCache.poll();
        if (helper == null) {
            helper = new BufferCacheHeaderHelper(pageSize);
        }
        return helper;
    }

    private void returnHeaderHelper(BufferCacheHeaderHelper buffer) {
        headerPageCache.offer(buffer);
    }

    void write(CachedPage cPage) throws HyracksDataException {
        BufferedFileHandle fInfo = getFileInfo(cPage);
        // synchronize on fInfo to prevent the file handle from being deleted until the page is written.
        synchronized (fInfo) {
            if (!fInfo.fileHasBeenDeleted()) {
                ByteBuffer buf = cPage.buffer.duplicate();
                final int totalPages = cPage.getFrameSizeMultiplier();
                final int extraBlockPageId = cPage.getExtraBlockPageId();
                final boolean contiguousLargePages = (BufferedFileHandle.getPageId(cPage.dpid) + 1) == extraBlockPageId;
                BufferCacheHeaderHelper header = checkoutHeaderHelper();
                try {
                    buf.limit(contiguousLargePages ? pageSize * totalPages : pageSize);
                    buf.position(0);
                    long bytesWritten = ioManager.syncWrite(fInfo.getFileHandle(),
                            getOffsetForPage(BufferedFileHandle.getPageId(cPage.dpid)),
                            header.prepareWrite(cPage, buf));

                    if (bytesWritten != (contiguousLargePages ? pageSize * (totalPages - 1) : 0)
                            + getPageSizeWithHeader()) {
                        throw new HyracksDataException("Failed to write completely: " + bytesWritten);
                    }
                } finally {
                    returnHeaderHelper(header);
                }
                if (totalPages > 1 && !contiguousLargePages) {
                    buf.limit(totalPages * pageSize);
                    ioManager.syncWrite(fInfo.getFileHandle(), getOffsetForPage(extraBlockPageId), buf);
                }
                assert buf.capacity() == (pageSize * totalPages);
            }
        }
    }

    @Override
    public void unpin(ICachedPage page) throws HyracksDataException {
        if (closed) {
            throw new HyracksDataException("unpin called on a closed cache");
        }
        int pinCount = ((CachedPage) page).pinCount.decrementAndGet();
        if (DEBUG && pinCount == 0) {
            pinnedPageOwner.remove(page);
        }
    }

    private int hash(long dpid) {
        int hashValue = (int) dpid ^ (Integer.reverse((int) (dpid >>> 32)) >>> 1);
        return hashValue % pageMap.length;
    }

    private static class CacheBucket {
        private final Lock bucketLock;
        private CachedPage cachedPage;

        public CacheBucket() {
            bucketLock = new ReentrantLock();
        }
    }

    @Override
    public ICachedPageInternal getPage(int cpid) {
        synchronized (cachedPages) {
            return cachedPages.get(cpid);
        }
    }

    private class CleanerThread implements Runnable {
        private volatile boolean shutdownStart = false;
        private volatile boolean shutdownComplete = false;
        private final Object threadLock = new Object();
        private final Object cleanNotification = new Object();
        // Simply keeps incrementing this counter when a page is cleaned.
        // Used to implement wait-for-cleanerthread heuristic optimizations.
        // A waiter can detect whether pages have been cleaned.
        private volatile int cleanedCount = 0;

        public void cleanPage(CachedPage cPage, boolean force) {
            if (cPage.dirty.get() && !cPage.confiscated.get()) {
                boolean proceed = false;
                if (force) {
                    cPage.latch.writeLock().lock();
                    proceed = true;
                } else {
                    proceed = cPage.latch.readLock().tryLock();
                }
                if (proceed) {
                    try {
                        cleanPageLocked(cPage);
                    } finally {
                        if (force) {
                            cPage.latch.writeLock().unlock();
                        } else {
                            cPage.latch.readLock().unlock();
                        }
                    }
                } else if (shutdownStart) {
                    throw new IllegalStateException(
                            "Cache closed, but unable to acquire read lock on dirty page: " + cPage.dpid);
                }
            }
        }

        private void cleanPageLocked(CachedPage cPage) {
            // Make sure page is still dirty.
            if (!cPage.dirty.get()) {
                return;
            }
            boolean cleaned = true;
            try {
                write(cPage);
            } catch (HyracksDataException e) {
                LOGGER.log(Level.WARNING, "Unable to write dirty page", e);
                cleaned = false;
            }
            if (cleaned) {
                cPage.dirty.set(false);
                cPage.pinCount.decrementAndGet();
                // this increment of a volatile is OK as there is only one writer
                cleanedCount++;
                synchronized (cleanNotification) {
                    cleanNotification.notifyAll();
                }
            }
        }

        @Override
        public void run() {
            synchronized (threadLock) {
                try {
                    while (!shutdownStart) {
                        runCleanCycle();
                    }
                } catch (InterruptedException e) {

                    Thread.currentThread().interrupt();
                } finally {
                    shutdownComplete = true;
                    threadLock.notifyAll();
                }
            }
        }

        private void runCleanCycle() throws InterruptedException {
            pageCleanerPolicy.notifyCleanCycleStart(threadLock);
            int curPage = 0;
            while (true) {
                synchronized (cachedPages) {
                    if (curPage >= cachedPages.size()) {
                        break;
                    }
                    CachedPage cPage = (CachedPage) cachedPages.get(curPage);
                    if (cPage != null) {
                        cleanPage(cPage, false);
                    }
                }
                curPage++;
            }
            if (!shutdownStart) {
                pageCleanerPolicy.notifyCleanCycleFinish(threadLock);
            }
        }
    }

    @Override
    public void close() {
        closed = true;
        fifoWriter.destroyQueue();
        try {
            synchronized (cleanerThread.threadLock) {
                cleanerThread.shutdownStart = true;
                cleanerThread.threadLock.notifyAll();
                while (!cleanerThread.shutdownComplete) {
                    cleanerThread.threadLock.wait();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        synchronized (fileInfoMap) {
            for (Map.Entry<Integer, BufferedFileHandle> entry : fileInfoMap.entrySet()) {
                try {
                    boolean fileHasBeenDeleted = entry.getValue().fileHasBeenDeleted();
                    sweepAndFlush(entry.getKey(), !fileHasBeenDeleted);
                    if (!fileHasBeenDeleted) {
                        ioManager.close(entry.getValue().getFileHandle());
                    }
                } catch (HyracksDataException e) {
                    if (LOGGER.isLoggable(Level.WARNING)) {
                        LOGGER.log(Level.WARNING, "Error flushing file id: " + entry.getKey(), e);
                    }
                }
            }
            fileInfoMap.clear();
        }
    }

    @Override
    public int createFile(FileReference fileRef) throws HyracksDataException {
        if (LOGGER.isLoggable(fileOpsLevel)) {
            LOGGER.log(fileOpsLevel, "Creating file: " + fileRef + " in cache: " + this);
        }
        IoUtil.create(fileRef);
        try {
            synchronized (fileInfoMap) {
                return fileMapManager.registerFile(fileRef);
            }
        } catch (Exception e) {
            // If file registration failed for any reason, we need to undo the file creation
            try {
                IoUtil.delete(fileRef);
            } catch (Exception deleteException) {
                e.addSuppressed(deleteException);
            }
            throw HyracksDataException.create(e);
        }
    }

    @Override
    public int openFile(FileReference fileRef) throws HyracksDataException {
        if (LOGGER.isLoggable(fileOpsLevel)) {
            LOGGER.log(fileOpsLevel, "Opening file: " + fileRef + " in cache: " + this);
        }
        int fileId;
        synchronized (fileInfoMap) {
            if (fileMapManager.isMapped(fileRef)) {
                fileId = fileMapManager.lookupFileId(fileRef);
            } else {
                fileId = fileMapManager.registerFile(fileRef);
            }
            openFile(fileId);
        }
        return fileId;
    }

    @Override
    public void openFile(int fileId) throws HyracksDataException {
        if (LOGGER.isLoggable(fileOpsLevel)) {
            LOGGER.log(fileOpsLevel, "Opening file: " + fileId + " in cache: " + this);
        }
        synchronized (fileInfoMap) {
            BufferedFileHandle fInfo;
            fInfo = fileInfoMap.get(fileId);
            if (fInfo == null) {
                boolean unreferencedFileFound = true;
                while (fileInfoMap.size() >= maxOpenFiles && unreferencedFileFound) {
                    // map is full, make room by cleaning up unreferenced files
                    unreferencedFileFound = false;
                    for (Map.Entry<Integer, BufferedFileHandle> entry : fileInfoMap.entrySet()) {
                        if (entry.getValue().getReferenceCount() <= 0) {
                            int entryFileId = entry.getKey();
                            boolean fileHasBeenDeleted = entry.getValue().fileHasBeenDeleted();
                            sweepAndFlush(entryFileId, !fileHasBeenDeleted);
                            if (!fileHasBeenDeleted) {
                                ioManager.close(entry.getValue().getFileHandle());
                            }
                            fileInfoMap.remove(entryFileId);
                            unreferencedFileFound = true;
                            // for-each iterator is invalid because we changed
                            // fileInfoMap
                            break;
                        }
                    }
                }
                if (fileInfoMap.size() >= maxOpenFiles) {
                    throw new HyracksDataException("Could not open fileId " + fileId + ". Max number of files "
                            + maxOpenFiles + " already opened and referenced.");
                }
                // create, open, and map new file reference
                FileReference fileRef = fileMapManager.lookupFileName(fileId);
                IFileHandle fh = ioManager.open(fileRef, IIOManager.FileReadWriteMode.READ_WRITE,
                        IIOManager.FileSyncMode.METADATA_ASYNC_DATA_ASYNC);
                fInfo = new BufferedFileHandle(fileId, fh);
                fileInfoMap.put(fileId, fInfo);
            }
            fInfo.incReferenceCount();
        }
    }

    private void sweepAndFlush(int fileId, boolean flushDirtyPages) throws HyracksDataException {
        for (final CacheBucket bucket : pageMap) {
            bucket.bucketLock.lock();
            try {
                CachedPage prev = bucket.cachedPage;
                while (prev != null) {
                    CachedPage cPage = prev.next;
                    if (cPage == null) {
                        break;
                    }
                    if (invalidateIfFileIdMatch(fileId, cPage, flushDirtyPages)) {
                        prev.next = cPage.next;
                        cPage.next = null;
                    } else {
                        prev = cPage;
                    }
                }
                // Take care of the head of the chain.
                if (bucket.cachedPage != null && invalidateIfFileIdMatch(fileId, bucket.cachedPage, flushDirtyPages)) {
                    CachedPage cPage = bucket.cachedPage;
                    bucket.cachedPage = bucket.cachedPage.next;
                    cPage.next = null;
                }
            } finally {
                bucket.bucketLock.unlock();
            }
        }
    }

    private boolean invalidateIfFileIdMatch(int fileId, CachedPage cPage, boolean flushDirtyPages)
            throws HyracksDataException {
        if (BufferedFileHandle.getFileId(cPage.dpid) == fileId) {
            int pinCount;
            if (cPage.dirty.get()) {
                if (flushDirtyPages) {
                    write(cPage);
                }
                cPage.dirty.set(false);
                pinCount = cPage.pinCount.decrementAndGet();
            } else {
                pinCount = cPage.pinCount.get();
            }
            if (pinCount > 0) {
                throw new IllegalStateException("Page " + BufferedFileHandle.getFileId(cPage.dpid) + ":"
                        + BufferedFileHandle.getPageId(cPage.dpid)
                        + " is pinned and file is being closed. Pincount is: " + pinCount + " Page is confiscated: "
                        + cPage.confiscated);
            }
            cPage.invalidate();
            return true;
        }
        return false;
    }

    @Override
    public void closeFile(int fileId) throws HyracksDataException {
        if (LOGGER.isLoggable(fileOpsLevel)) {
            LOGGER.log(fileOpsLevel, "Closing file: " + fileId + " in cache: " + this);
        }
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine(dumpState());
        }

        synchronized (fileInfoMap) {
            BufferedFileHandle fInfo = fileInfoMap.get(fileId);
            if (fInfo == null) {
                throw new HyracksDataException("Closing unopened file");
            }
            if (fInfo.decReferenceCount() < 0) {
                throw new HyracksDataException("Closed fileId: " + fileId + " more times than it was opened.");
            }
        }
        if (LOGGER.isLoggable(fileOpsLevel)) {
            LOGGER.log(fileOpsLevel, "Closed file: " + fileId + " in cache: " + this);
        }
    }

    @Override
    public void flushDirtyPage(ICachedPage page) throws HyracksDataException {
        // Assumes the caller has pinned the page.
        cleanerThread.cleanPage((CachedPage) page, true);
    }

    @Override
    public void force(int fileId, boolean metadata) throws HyracksDataException {
        BufferedFileHandle fInfo;
        synchronized (fileInfoMap) {
            fInfo = fileInfoMap.get(fileId);
        }
        ioManager.sync(fInfo.getFileHandle(), metadata);
    }

    @Override
    public void deleteFile(FileReference fileRef) throws HyracksDataException {
        synchronized (fileInfoMap) {
            if (fileMapManager.isMapped(fileRef)) {
                int fileId = fileMapManager.lookupFileId(fileRef);
                deleteFile(fileId);
                return;
            } else {
                IoUtil.delete(fileRef);
            }
        }
    }

    @Override
    public void deleteFile(int fileId) throws HyracksDataException {
        if (LOGGER.isLoggable(fileOpsLevel)) {
            LOGGER.log(fileOpsLevel, "Deleting file: " + fileId + " in cache: " + this);
        }
        synchronized (fileInfoMap) {
            sweepAndFlush(fileId, false);
            BufferedFileHandle fInfo = null;
            try {
                fInfo = fileInfoMap.get(fileId);
                if (fInfo != null && fInfo.getReferenceCount() > 0) {
                    throw new HyracksDataException("Deleting open file");
                }
            } catch (Exception e) {
                throw HyracksDataException.create(e);
            } finally {
                FileReference fileRef = fileMapManager.unregisterFile(fileId);
                try {
                    if (fInfo != null) {
                        // Mark the fInfo as deleted,
                        // such that when its pages are reclaimed in openFile(),
                        // the pages are not flushed to disk but only invalidated.
                        synchronized (fInfo) {
                            if (!fInfo.fileHasBeenDeleted()) {
                                ioManager.close(fInfo.getFileHandle());
                                fInfo.markAsDeleted();
                            }
                        }
                    }
                } finally {
                    IoUtil.delete(fileRef);
                }
            }
        }
    }

    @Override
    public synchronized int getFileReferenceCount(int fileId) {
        synchronized (fileInfoMap) {
            BufferedFileHandle fInfo = fileInfoMap.get(fileId);
            if (fInfo != null) {
                return fInfo.getReferenceCount();
            } else {
                return 0;
            }
        }
    }

    @Override
    public void start() {
        // no op
    }

    @Override
    public void stop(boolean dumpState, OutputStream os) throws IOException {
        if (dumpState) {
            dumpState(os);
        }
        close();
    }

    @Override
    public void addPage(ICachedPageInternal page) {
        synchronized (cachedPages) {
            final int cpid = page.getCachedPageId();
            if (cpid < cachedPages.size()) {
                cachedPages.set(cpid, page);
            } else {
                if (cpid > cachedPages.size()) {
                    // 4 > 1 -> [exiting, null, null, null, new]
                    cachedPages.addAll(Collections.nCopies(cpid - cachedPages.size(), null));
                }
                cachedPages.add(page);
            }
        }
    }

    @Override
    public boolean removePage(ICachedPageInternal victimPage) {
        CachedPage victim = (CachedPage) victimPage;
        // Case 1 from findPage()
        if (victim.dpid < 0) { // new page
            if (!victim.pinCount.compareAndSet(0, 1)) {
                return false;
            }
            // now that we have the pin, ensure the victim's dpid still is < 0, if it's not, decrement
            // pin count and try again
            if (victim.dpid >= 0) {
                victim.pinCount.decrementAndGet();
                return false;
            }
        } else {
            // Case 2a/b
            int pageHash = hash(victim.dpid);
            CacheBucket bucket = pageMap[pageHash];
            bucket.bucketLock.lock();
            try {
                if (!victim.pinCount.compareAndSet(0, 1)) {
                    return false;
                }
                // now that we have the pin, ensure the victim's bucket hasn't changed, if it has, decrement
                // pin count and try again
                if (pageHash != hash(victim.dpid)) {
                    victim.pinCount.decrementAndGet();
                    return false;
                }
                // readjust the next pointers to remove this page from
                // the pagemap
                CachedPage curr = bucket.cachedPage;
                CachedPage prev = null;
                boolean found = false;
                //traverse the bucket's linked list to find the victim.
                while (curr != null) {
                    if (curr == victim) {
                        // we found where the victim
                        // resides in the hash table
                        if (DEBUG) {
                            assert curr != curr.next;
                        }
                        if (prev == null) {
                            // if this is the first page in the bucket
                            bucket.cachedPage = curr.next;
                        } else {
                            // if it isn't we need to make the previous
                            // node point to where it should
                            prev.next = curr.next;
                            if (DEBUG) {
                                assert prev.next != prev;
                            }
                        }
                        curr.next = null;
                        found = true;
                        break;
                    }
                    // go to the next entry
                    prev = curr;
                    curr = curr.next;
                }
                assert found;
            } finally {
                bucket.bucketLock.unlock();
            }
        }
        synchronized (cachedPages) {
            ICachedPageInternal old = cachedPages.set(victim.cpid, null);
            if (DEBUG) {
                assert old == victim;
            } ;
        }
        return true;
    }

    @Override
    public void dumpState(OutputStream os) throws IOException {
        os.write(dumpState().getBytes());
    }

    @Override
    public int getNumPagesOfFile(int fileId) throws HyracksDataException {
        synchronized (fileInfoMap) {
            BufferedFileHandle fInfo = fileInfoMap.get(fileId);
            if (fInfo == null) {
                throw new HyracksDataException("No such file mapped for fileId:" + fileId);
            }
            if (DEBUG) {
                assert ioManager.getSize(fInfo.getFileHandle()) % getPageSizeWithHeader() == 0;
            }
            return (int) (ioManager.getSize(fInfo.getFileHandle()) / getPageSizeWithHeader());
        }
    }

    @Override
    public void adviseWontNeed(ICachedPage page) {
        pageReplacementStrategy.adviseWontNeed((ICachedPageInternal) page);
    }

    @Override
    public ICachedPage confiscatePage(long dpid) throws HyracksDataException {
        return confiscatePage(dpid, 1);
    }

    @Override
    public ICachedPage confiscateLargePage(long dpid, int multiplier, int extraBlockPageId)
            throws HyracksDataException {
        ICachedPage cachedPage = confiscatePage(dpid, multiplier);
        ((ICachedPageInternal) cachedPage).setExtraBlockPageId(extraBlockPageId);
        return cachedPage;
    }

    private ICachedPage confiscatePage(long dpid, int multiplier) throws HyracksDataException {
        return getPageLoop(dpid, multiplier, true);
    }

    private ICachedPage confiscateInner(long dpid, int multiplier) {
        ICachedPage returnPage = null;
        CachedPage victim = (CachedPage) pageReplacementStrategy.findVictim(multiplier);
        if (victim == null) {
            return victim;
        }
        if (DEBUG) {
            assert !victim.confiscated.get();
        }
        // find a page that would possibly be evicted anyway
        // Case 1 from findPage()
        if (victim.dpid < 0) { // new page
            if (!victim.pinCount.compareAndSet(0, 1)) {
                return null;
            }
            // now that we have the pin, ensure the victim's dpid still is < 0, if it's not, decrement
            // pin count and try again
            if (victim.dpid >= 0) {
                victim.pinCount.decrementAndGet();
                return null;
            }
            returnPage = victim;
            ((CachedPage) returnPage).dpid = dpid;
        } else {
            // Case 2a/b
            int pageHash = hash(victim.getDiskPageId());
            CacheBucket bucket = pageMap[pageHash];
            bucket.bucketLock.lock();
            try {
                // readjust the next pointers to remove this page from
                // the pagemap
                CachedPage curr = bucket.cachedPage;
                CachedPage prev = null;
                boolean found = false;
                //traverse the bucket's linked list to find the victim.
                while (curr != null) {
                    if (curr == victim) { // we found where the victim
                        // resides in the hash table
                        if (!victim.pinCount.compareAndSet(0, 1)) {
                            break;
                        }
                        if (DEBUG) {
                            assert curr != curr.next;
                        }
                        if (prev == null) {
                            // if this is the first page in the bucket
                            bucket.cachedPage = curr.next;
                            // if it isn't we need to make the previous
                            // node point to where it should
                        } else {
                            prev.next = curr.next;
                            if (DEBUG) {
                                assert prev.next != prev;
                            }
                        }
                        curr.next = null;
                        found = true;
                        break;
                    }
                    // go to the next entry
                    prev = curr;
                    curr = curr.next;
                }
                if (found) {
                    returnPage = victim;
                    ((CachedPage) returnPage).dpid = dpid;
                } //otherwise, someone took the same victim before we acquired the lock. try again!
            } finally {
                bucket.bucketLock.unlock();
            }
        }
        // if we found a page after all that, go ahead and finish
        if (returnPage != null) {
            ((CachedPage) returnPage).confiscated.set(true);
            if (DEBUG) {
                confiscateLock.lock();
                try {
                    confiscatedPages.add((CachedPage) returnPage);
                    confiscatedPagesOwner.put((CachedPage) returnPage, Thread.currentThread().getStackTrace());
                } finally {
                    confiscateLock.unlock();
                }
            }
            return returnPage;
        }
        return null;
    }

    private ICachedPage getPageLoop(long dpid, int multiplier, boolean confiscate) throws HyracksDataException {
        final long startingPinCount = DEBUG ? masterPinCount.get() : -1;
        int cycleCount = 0;
        try {
            while (true) {
                cycleCount++;
                int startCleanedCount = cleanerThread.cleanedCount;
                ICachedPage page = confiscate ? confiscateInner(dpid, multiplier) : findPageInner(dpid);
                if (page != null) {
                    masterPinCount.incrementAndGet();
                    return page;
                }
                // no page available to confiscate. try kicking the cleaner thread.
                synchronized (cleanerThread.threadLock) {
                    try {
                        pageCleanerPolicy.notifyVictimNotFound(cleanerThread.threadLock);
                    } catch (InterruptedException e) {
                        // Re-interrupt the thread so this gets handled later
                        Thread.currentThread().interrupt();
                    }
                }
                // Heuristic optimization. Check whether the cleaner thread has
                // cleaned pages since we did our last pin attempt.
                if (cleanerThread.cleanedCount - startCleanedCount > MIN_CLEANED_COUNT_DIFF) {
                    // Don't go to sleep and wait for notification from the cleaner,
                    // just try to pin again immediately.
                    continue;
                }
                synchronized (cleanerThread.cleanNotification) {
                    try {
                        // it's OK to not loop on this wait, as we do not rely on any condition to be true on notify
                        // This seemingly pointless loop keeps SonarQube happy
                        do {
                            cleanerThread.cleanNotification.wait(PIN_MAX_WAIT_TIME);
                        } while (false);
                    } catch (InterruptedException e) {
                        // Re-interrupt the thread so this gets handled later
                        Thread.currentThread().interrupt();
                    }
                }
                finishQueue();
                if (cycleCount > MAX_PIN_ATTEMPT_CYCLES) {
                    cycleCount = 0; // suppress warning below
                    throw new HyracksDataException("Unable to find free page in buffer cache after "
                            + MAX_PIN_ATTEMPT_CYCLES + " cycles (buffer cache undersized?)"
                            + (DEBUG ? " ; " + (masterPinCount.get() - startingPinCount)
                                    + " successful pins since start of cycle" : ""));
                }
            }
        } finally {
            if (cycleCount > PIN_ATTEMPT_CYCLES_WARNING_THRESHOLD && LOGGER.isLoggable(Level.WARNING)) {
                LOGGER.warning("Took " + cycleCount + " cycles to find free page in buffer cache.  (buffer cache "
                        + "undersized?)" + (DEBUG ? " ; " + (masterPinCount.get() - startingPinCount)
                                + " successful pins since start of cycle" : ""));
            }
        }
    }

    @Override
    public void returnPage(ICachedPage page) {
        returnPage(page, true);
    }

    @Override
    public void returnPage(ICachedPage page, boolean reinsert) {
        CachedPage cPage = (CachedPage) page;
        CacheBucket bucket;
        if (!page.confiscated()) {
            return;
        }
        if (reinsert) {
            int hash = hash(cPage.dpid);
            bucket = pageMap[hash];
            bucket.bucketLock.lock();
            if (DEBUG) {
                confiscateLock.lock();
            }
            try {
                cPage.reset(cPage.dpid);
                cPage.valid = true;
                cPage.next = bucket.cachedPage;
                bucket.cachedPage = cPage;
                cPage.pinCount.decrementAndGet();
                if (DEBUG) {
                    assert cPage.pinCount.get() == 0;
                    assert cPage.latch.getReadLockCount() == 0;
                    assert cPage.latch.getWriteHoldCount() == 0;
                    confiscatedPages.remove(cPage);
                    confiscatedPagesOwner.remove(cPage);
                }
            } finally {
                bucket.bucketLock.unlock();
                if (DEBUG) {
                    confiscateLock.unlock();
                }
            }
        } else {
            cPage.invalidate();
            cPage.pinCount.decrementAndGet();
            if (DEBUG) {
                assert cPage.pinCount.get() == 0;
                assert cPage.latch.getReadLockCount() == 0;
                assert cPage.latch.getWriteHoldCount() == 0;
                confiscateLock.lock();
                try {
                    confiscatedPages.remove(cPage);
                    confiscatedPagesOwner.remove(cPage);
                } finally {
                    confiscateLock.unlock();
                }
            }
        }
        pageReplacementStrategy.adviseWontNeed(cPage);
    }

    @Override
    public void setPageDiskId(ICachedPage page, long dpid) {
        ((CachedPage) page).dpid = dpid;
    }

    @Override
    public IFIFOPageQueue createFIFOQueue() {
        return fifoWriter.createQueue(FIFOLocalWriter.instance());
    }

    @Override
    public void finishQueue() {
        fifoWriter.finishQueue();
    }

    @Override
    public boolean isReplicationEnabled() {
        if (ioReplicationManager != null) {
            return ioReplicationManager.isReplicationEnabled();
        }
        return false;
    }

    @Override
    public IIOReplicationManager getIOReplicationManager() {
        return ioReplicationManager;
    }

    @Override
    /**
     * _ONLY_ call this if you absolutely, positively know this file has no dirty pages in the cache!
     * Bypasses the normal lifecycle of a file handle and evicts all references to it immediately.
     */
    public void purgeHandle(int fileId) throws HyracksDataException {
        synchronized (fileInfoMap) {
            BufferedFileHandle fh = fileInfoMap.get(fileId);
            if (fh != null) {
                ioManager.close(fh.getFileHandle());
                fileInfoMap.remove(fileId);
                fileMapManager.unregisterFile(fileId);
            }
        }
    }

    static class BufferCacheHeaderHelper {
        private static final int FRAME_MULTIPLIER_OFF = 0;
        private static final int EXTRA_BLOCK_PAGE_ID_OFF = FRAME_MULTIPLIER_OFF + 4; // 4

        private final ByteBuffer buf;
        private final ByteBuffer[] array;

        private BufferCacheHeaderHelper(int pageSize) {
            buf = ByteBuffer.allocate(RESERVED_HEADER_BYTES + pageSize);
            array = new ByteBuffer[] { buf, null };
        }

        private ByteBuffer[] prepareWrite(CachedPage cPage, ByteBuffer pageBuffer) {
            buf.position(0);
            buf.limit(RESERVED_HEADER_BYTES);
            buf.putInt(FRAME_MULTIPLIER_OFF, cPage.getFrameSizeMultiplier());
            buf.putInt(EXTRA_BLOCK_PAGE_ID_OFF, cPage.getExtraBlockPageId());
            array[1] = pageBuffer;
            return array;
        }

        private ByteBuffer prepareRead() {
            buf.position(0);
            buf.limit(buf.capacity());
            return buf;
        }

        private int processRead(CachedPage cPage) {
            buf.position(RESERVED_HEADER_BYTES);
            cPage.buffer.position(0);
            cPage.buffer.put(buf);
            int multiplier = buf.getInt(FRAME_MULTIPLIER_OFF);
            cPage.setFrameSizeMultiplier(multiplier);
            cPage.setExtraBlockPageId(buf.getInt(EXTRA_BLOCK_PAGE_ID_OFF));
            return multiplier;
        }
    }
}


Bienvenido (Welcome)! Corina and I, my two dogs LaLa and LuLu , have been living in Quanacaste, Costa Rica for almost two years. It has been a great adventure; visiting small towns and the two largest cities in Costa Rica; San Jose, the capital of Costa Rica and Liberia. They are the two largest cities in Costa Rica and easy to get lost in. I like to get lost in Costa Rica. I give myself plenty of daylight; I do not drive at night unless absolutely necessary. My eyesight is not what it used to be and prefer the daytime. Not all roads have good lighting to see your way.
Being lost in San Jose was a little frightening at first. Some of the roads were confusing (even GPS would get us more lost, but the people are friendly and some would guide you at no cost to them, besides they were walking home, and we were going that way. San Jose is huge easy to get lost. It is a major city, that is why we opted for Lake Arenal where it is more rural and three and half ours away but two hours from the ocean and beaches.
Visiting the beaches, you travel through Liberia pass the airport. We love the area we live in; Aguacate is a quite little area in the hills of the rain forest. Most days we wake to the sounds of birds, monkeys, and dogs. Lots of dogs ran free in this part of Costa Rica. We built a fence to keep dogs off our property and our puppies safe. You can see parts of Lake Arenal from our porch which is nice to set on and watch the activity in the area. The rainy season is long with the heaviest rain coming in October through December. You must enjoy rain to live in this area of Costa Rica. Some areas get a lot less rain than we do: the beaches on the Pacific coast, central Costa Rica ( San Ramon area has a mild climate and less rain). I love Costa Rica; the freshness, beauty and the wonderful sounds that make the country so relaxing.
Until next time, PURA VIDA!
Magnificent in the blue skies.
Would love to read your poetry!


text:
Mount Noice är ett berg i Antarktis. Det ligger i Östantarktis. Nya Zeeland gör anspråk på området. Toppen på Mount Noice är  meter över havet, eller  meter över den omgivande terrängen. Bredden vid basen är  km.

Terrängen runt Mount Noice är varierad. Den högsta punkten i närheten är Overlord,  meter över havet,  km norr om Mount Noice. Trakten är obefolkad. Det finns inga samhällen i närheten.

Kommentarer

Källor

Berg i Östantarktis
Nya Zeelands anspråk i Antarktis
Berg i Antarktis 2000 meter över havet eller högre

redpajama_set_name: RedPajamaWikipedia


text:
← Antique language, plus breasts.
The un-election. →
Open thread.
I've got a jam-packed morning, leading into a less-packed afternoon, and then a jam-packed evening. So I'm leaving you, my chatty friends, with an open thread to keep you all amused. I don't have much in the way of conversation-starters, but how about this? For the first time in a very long time — it feels like…four years — I'm spending more time watching cable TV news than I usually do, by a factor of Quite a Lot.
And you know what? Every year, it gets worse. It's like there's no bottom.
Jack Shafer notes the peculiar ubiquity of CNN's lame-ass slogan. Start there, and discuss.
And now, off to Wayne State, where for some reason, a class of journalism students wants to hear what I have to say. I'll school 'em, by God.
Posted at 8:45 am in Media |
29 responses to "Open thread."
brian stouder said on January 10, 2008 at 9:31 am
Hey – and the whole presidential shebang is headed right for you (if not already there, in Michigan) – and despite the D threat to turn away the red-headed-stepchild Michigan delegates, in a year like this – that ain't gonna happen! (no matter HOW much Howard the Duck Dean screams!)
I bet you and the journalism students can soon brag that you have "The Best Informal Politics Blogging Team In America"!
Anyway – I, too, am watching lots o' cable news lately…and the very BEST of the lot tends to be C-SPAN! Not black-backdrop talking head C-SPAN, but the C-SPAN live feeds of campaign events. I LOVE those!! After the candidate (or spouse of candidate) finishes her or his speech, they work the rope lines, and the C-SPAN cameras and audio allow you to elbow right up into the conversations and jokes….and it is just superb stuff!
(but it does tend to drive the young folks out of the house, regardless what the weather is)
alex said on January 10, 2008 at 9:36 am
Lame it is, but not one-tenth as preposterous as the Fox slogan.
garmoore said on January 10, 2008 at 10:12 am
In reality, only half the presidential hoop-de-do is coming to Michigan. The Republicans have already begun, with McCain and Romney both making appearances around the state the past day or so. The Dems, on the other hand, are avoiding us like the plague. Why? The national party has stripped Michigan of its delegates for moving its primary to Jan. 15 without approval from the party, so Obama and Edwards (and maybe others) removed their names from the ballot. Clinton has said she won't appear in the state, either, although she remains on the ballot. So this race is essentially all Republican.
Kirk said on January 10, 2008 at 10:17 am
I, too, like to watch campaign events on C-SPAN, though they get tedious after a while. They usually remind me how much pain and nonsense a candidate is willing to endure as part of the quest for power.
C-SPAN's perspective is insightful and entertaining. I've gotten a boot out of watching the president, first lady and mr. and mrs. visiting prime minister in the receiving line at White House state dinners.
brian stouder said on January 10, 2008 at 10:48 am
though they get tedious after a while. They usually remind me how much pain and nonsense a candidate is willing to endure as part of the quest for power.
I was watching Michelle Obama working the crowd at the end of an event in New Hampshire last weekend, and a cloying old (old old) guy began expounding at great length about something or other…and even after Ms Obama gave him several minutes – the guy said "and another thing…" – and as I was sighing, Obama put her hand on her collar and left it there. Within a few seconds, someone brushed in between and moved things along; clearly, THAT was the signal!
(and, Michelle Obama is much more receptive than I'd be)
ashley said on January 10, 2008 at 12:01 pm
A buddy of mine took a job at Wayne State after he got his PhD. He got shanked in the parking lot his 2nd year there. NOLA may be the murder capital, but I gots to give you props — y'all kick ass on non-lethal crime.
del said on January 10, 2008 at 12:23 pm
Fox's "Fair and Balanced" takes the cake. Though CNN is rushing to the bottom too (witness Glenn Beck). Some would call it the "Fox effect." They're all so weak. I watched Chris Matthews on MSNBC the other night (dunno why) and he kept insinuating, over and over, that Hillary's team somehow cheated in NH — how else could the pollsters have got it so wrong? It's just amazing how everything that woman does is completely distorted so as to be evil. When she's calm and collected she's the Ice Queen, when she's emotional she's shedding Crocodile tears. Maybe the Trilateral Commision's to blame? Kinda feel sorry for her.
Kirk said on January 10, 2008 at 12:28 pm
Yes, the cable news networks, led by Fox, are being sucked toward the lowest common denominator. And 24 hours is a lot to fill. I'd settle for 10 minutes of cartoons every hour.
Remember the glory days of CNN? Around Bush War I, with Peter Arnett and Bernie Shaw were competent, reliable news sources? And we had eye candy like Lynne Russell. Gone. When Bernie left and they started basing the talking head lineup more on prettyness than competence, they went down the toilet.
And I really hate to tell them, but the most respected name in news is probably the BBC.
Kim said on January 10, 2008 at 12:50 pm
If CNN would use the word "shanked" in context with the Hillary-bashing over every freaking thing the woman says/does I might watch more often. As it is, every time I do watch and my husband is near I say, "Hey, doesn't John King look like Elvis?" or "Do you think Anderson Cooper could fly as a gay man on the networks?" or "Doesn't Larry King look cadaverous tonight?"
Nancy, I hope you took the opportunity to use the "Never wear a miniskirt to a train wreck" line when asked for advice from the earnest journalists. Hey – that's a great band name.
Danny said on January 10, 2008 at 1:17 pm
You guys are hilarious. Hope no one tripped over their feet rushing to the keyboard to bash Fox News. I mean, I soon as I read Nancy's post, I knew before I clicked that Alex had weighed in before his first cup. Reflexive tripe is what it is. I'm surpised you forgot to blame breast cancer on Bush and Cheney yesterday. What a disappointment. You're slipping.
And note, I'm not defending Fox as the high example of journalistic virtue, I'm just saying that CNN and MSNBC blew large juicy chunks way before bashing Fox News was even in vogue and the likes of Chris Matthews, Larry King and Keith Olberman are to blame. Keith has yet to recognize that when he has his weekly award for "Worst Human Being Ever" that he is searching for something that has already found him.
alex said on January 10, 2008 at 1:55 pm
Danny, take your reflexive tripe, fold it five ways and pound it where that big stick is.
Kirk said on January 10, 2008 at 2:03 pm
Reflexive tripe is what that O'Nazi guy dishes out on Fox.
ashley said on January 10, 2008 at 2:07 pm
Can we all get along?
Laura said on January 10, 2008 at 2:15 pm
Danny,
I blame Bush and Cheney for breast cancer. Mostly Cheney.
brian stouder said on January 10, 2008 at 2:22 pm
Say – here's an entertaining diversion…an 11 question quizz that will take about 2 minutes to zip through, and which helps you to select the presidential candidate that most closely matches your positions on the issues.
What I especially liked was that you also get to assign the importance level of each issue.
I scored a 30, and Obama was listed as the candidate that best suited me…so I immediately liked the thing!
http://www.wqad.com/Global/link.asp?L=259460
These quizzes that try to map your views to a candidate's views almost always omit questions about the rebuilding of New Orleans, the Gulf Coast, the failure of the federally built levees, and so on.
That's my hot button issue, and the only person really making a point to address that issue is Edwards. HRC will talk a good game when she's in New Orleans, and not say word one about it when she's not here.
So IMHO, the quiz is flawed to the point of being useless.
LAMary said on January 10, 2008 at 2:55 pm
Last August when a judge in Detroit ruled against the Bush administration in the case regarding warrantless phone tapping, NPR said, " A Detroit judge has ruled against warrantless phone tapping." Fox News led the story with, " Another blow to the war on terror." This was on one of their "news" programs, not one of the programs they label as commentary.
Mouse said on January 10, 2008 at 6:14 pm
Jeez,where the hell is Michaelj when you need him.This is a great set-up for one of his rants.
del said on January 10, 2008 at 8:43 pm
Okay, for the cause of peace I will lay off Fox. Tonight I watched a little bit of Tucker Carlson. Boy was he infuriating. Twisted everything. I think Jon Stewart was right about him.
Is Colbert back on the air? He's gonna be a speaker at the
Democratic National Convention. And for you political junkies, here in Michigan many Dems are going to be crossing over to vote in the Republican primary as our Democratic delegates aren't going to be recognized at the convention.
basset said on January 10, 2008 at 9:49 pm
>>Hope no one tripped over their feet rushing to the keyboard to bash Fox News.
no, it's too easy. they're probably the best marketing campaign of the last decade, though… Roger Ailes is an evil genius.
And that, based on a bunch of prior threads, is the last word on this one. Go start another, I can stop it in its tracks with even the briefest of posts.
brian stouder said on January 10, 2008 at 10:19 pm
See, when you post later than the 20th comment on a thread, the odds are that you could be the guy stuck shutting off the lights….and when you are post #39 (like yesterday) – it's almost a sure thing! – especially when you have a nifty story like the one about the non-basketball fan relative who successfully wins office pools by choosing on the basis of team colors!
An anecdote that good ends a thread on a high note…you aren't a thread killer, but instead a finishing craftsman. (in baseball or in field sales…or in blogging! – "the Closer" is the star!)
Jeff said on January 11, 2008 at 9:08 am
Mild bafflement . . . last night i found where Fox News is on the cable plan we have, thanks to the channel guide, to watch the SC GOP debate. Haven't watched Fox for months, if not years, and hardly know anyone who does. Looks like MSNBC on a budget, halfway between local news with all the 20-somethings all at a conference letting the adults have airtime, and CNN twenty years ago, but with current logo animations and sound effects.
What is all the big deal, anyhow? Of course, there's some faux Irishman everyone talks about of whom i can proudly say is not a factor in my life, having never seen his show — unless you count SNL parody takes. Or is it just that people are bothered by the fact that Roger Ailes has paying work that keeps him out of election campaigns? I say, keep the portly fellow off the streets and away from voters. (Frank Luntz really needs to stay away from the Green Room donuts.)
Danny said on January 11, 2008 at 12:22 pm
Jeff, good of you to have an opinion about a show that you have never watched.
I don't watch much television, but I think the big deal is, as usual, money. Fox, for whatever reason(s), seems to consistently beat everyone else … combined … in cable news ratings. And this has been going on for a few years now. And that means real money in terms of ad revenue.
This pisses off the other networks. And since Fox is the reverse of CNN and MSNBC where they have mostly liberals and a token conservative or two, it also pisses off people off who like their news with a more liberal slant.
Jeff said on January 11, 2008 at 1:40 pm
Danny, i thought my point was that i had no opinion, and couldn't see in a quick glance why so many have such a strong one.
And i still have no opinion! (SNL mocks Chris Matthews, too, but they never got around to Bill Moyers and "Now," which was ripe for the plucking, but they likely thought would be understood by few and laughed at by even less.)
Sorry, Jeff if I misunderstood. I thought you meant the "faux Irishman" as a perjorative. His show is sometimes OK, but I don't get the ratings. He cuts people off and the format is rushed. Just like Matthews' show.
I think the best show on FNC is Greta Van Sustren's "On the Record." But even that can grate when they are over-covering a celebrity case.
Fox is insidious. It creates public perceptions (more effectively than official propaganda in other countries). Does so in part by its focus, focusing, for example, on the positives in Iraq (school-building etc). D'ya think people want to watch stories about death and despair or hopeful rebuilding efforts? It also consistently editorializes to create a bond with the viewer (kinda like CNN's Nancy Grace or your local weathercaster or Bill Bonds for Detroiters). Fox panders to powerful and majoritarian interests thereby pulling a double-whammy, defending the status-quo (moneyed interests) and appealing to World Wrestling Federation loving public and the corporate advertisers who love them. Thread end. Kersplat.
Thread end. Kersplat.
Not so fast there, buckaroo!
I don't really disagree with the essence of the criticism of Fox news, but I DO argue that it is out of context.
My go-to cable news network is MSNBC. For whatever reason, I trust them. Their man Keith Olbermann loves loves loves trashing on all things Fox, as does Keith's boss Dan Abrams.
Recently, faux moralist Olbermann was on a riff about all the bikini-clad women that Fox manages to work into their feature stories and so on.
But I do flip the channel over to Fox with regularity, because every evening at a certain point, MSNBC becomes pedophile-central, or Prison Yards Are Us, or Gruesome Murder Hour. I'll take Greta or a Brit Hume replay over that stuff every time.
Honestly, CNN is only ever an after thought, although they WERE excellent on the night of the Iowa Caucuses, and we watched them almost exclusively (they had cameras inside various halls and homes hosting caucuses, and it was fascinating stuff)
And, not for nothing, as I recall, MSNBC was absolutely wall-to-wall rah-rah flag-waving excitement when we went to war in Iraq. I don't think KO had a show then, but most of the players there did.
By way of saying – this becomes a chicken-egg argument. Is the country more influenced by Fox News, or is Fox News shrewdly giving the country what it (already) wants? In any case, whatever O'Reilly's audience numbers actually are, they are absolutely miniscule compared to – say – the NFL (let alone the much-maligned broadcast network news broadcasts)
del said on January 13, 2008 at 1:01 am
Almost all the cable shows and news outlets were gung ho for the war. Part of that I blame on Fox, the Fox effect. (And like Colbert might say I could continue that Cheney is the devil, Bush has no soul and God is gay and I would be stealing Paul Krugman's material.) The media's stunning abdication of responsibility gave rise to Olbermann's righteous indignation. As for the pedophile, prison stuff on MSNBC I completely agree. Gotta turn it off so's I can find my happy place.
brian stouder • alex • garmoore • Kirk • brian stouder • ashley • del • Kirk • ashley • Kim • Danny • alex • Kirk • ashley • Laura • brian stouder • ashley • LAMary • Mouse • del • basset • brian stouder • Jeff • Danny • Jeff • Danny • del • brian stouder • del • and YOU.

redpajama_set_name: RedPajamaCommonCrawl


One of the many things our team looks for when inspecting a home is the presence of wood-boring insects. In the wild, many wood-boring insects are beneficial because they assist in the conversion of dead wood to soil. However, in our homes they are some of the most dangerous pests, as they can compromise the home's structural integrity. Depending on the climate of the region, certain wood-boring insects may be more or less prevalent. In Northern Michigan, the carpenter ant, which thrives in our relatively cool climate, is the most widespread pest of the wood-boring variety. This article seeks to be a primer on this species and its relationship to the home.
Carpenter ants are indigenous to nearly the entire continental United States but are most prevalent in forested areas. Unlike termites, the carpenter ant does not have the ability to consume wood. However, they do chew through wood (preferably dead & damp wood) and construct their nests in the chewed out portions of the wood, known as "galleries".


text:
Lena () är en flod i östra Sibirien. Floden är den tionde längsta i världen och har det nionde största avrinningsområdet (2 460 000 km²). Den är 4 400 kilometer lång. Namnet Lena tros komma från ordet Jelju-Ene, vilket betyder "den stora floden" på evenki, det språk som talas av nomadfolket evenker.

Geografi
Floden har sin källa 1 640 meter över havet i Bajkalbergen söder om det centralsibiriska höglandet, 20 kilometer väster om Bajkalsjön. Lena rinner åt nordost, förenar sig med bifloden Kirenga och floden Vitim. Den rinner ut på låglandet från och med Jakutsk, där den förenar sig med floden Oljokma och rinner sedan norr tills den förenar sig med floden Aldan. Efter att ha runnit nordväst på grund av bergskedjan Verchojansk förenar floden sig med sitt största tillflöde vilket är bifloden Viljuj för att rinna ut i Laptevhavet som är en del av Norra ishavet.

Vid utflödet har Lena ett floddelta på 10 800 km² som är 400 kilometer brett och floden delar upp sig i sju huvuddelflöden, där det viktigaste är det östligaste som heter Bylov. Detta delta är frusen tundra under sju månader av året, men i och med våren i maj övergår regionen till att bli ett bördigt våtmarksområde. Delar av deltat är naturskyddsområde.

Ekonomisk betydelse
Lena är navigerbar med båtar längs större delen av sin längd, när floden är isfri, och trafikeras av lastbilar på isen på vintern. Det finns bara två broar över floden, en vägbro och en järnvägsbro vid Ust-Kut, den enda hamnen med järnväg vid floden. Den största staden längs floden är Jakutsk. Vid mynningen är medelvattenföringen 16 300 m³/s. Det högsta uppmätta flödet är 190 000 m³/s, vilket är näst högst i världen och överträffas endast av Amazonfloden. Laxfiske förekommer i floden och vattenkvalitén är relativt bra, trots närliggande kolgruvor, guldgruvor och gasfyndigheter.

Referenser

Externa länkar

Vattendrag i Ryssland
Lenas avrinningsområde


the photos really don't do it justice – now i need to find a sealer of some description, or just be very damn careful. Fixing it to the stand tomorrow! Although this is leading me to thinking about perhaps installing a bunch of mutoscopes with increasingly suggestive titles, but the others having 'out of order' signs on them… maybe hanging them over the viewers… to be pondered….


Storrie Lake is one of those state parks that feels like home when I drive in. Bluewater Lake is the same way. And also Quartzsite. So far, my three favorite places to be. I like all the places I've stayed, otherwise I would have rolled on to greener pastures (or sandier landscapes), but these three give me a special feeling when I arrive.
We've been here a week now, and are in our third site. (You know how I am!) When we arrived last Saturday at 11 am, Memorial Day Weekend, I couldn't believe we actually got a developed site overlooking the water. It was almost level and had a shelter and bar-b-que ring.
Unfortunately, a big group camped next to us kept using the areas around my campsite as football practice and behind The Palms as a baseball diamond. I was afraid The Palms would get hit, and they didn't seem to want to move out further into the field behind us, so I moved on Monday morning to a site off my myself. Then in the middle of the week, I decided to move into the main loop, where there were a few non-reservable sites available. Katie and I are happily settled here for the duration.
We've been taking lots of walks with Jeanne and Riley, who got here right after we did. She did a bit of campsite moving, too, and she's now settled in at the other end of this loop. We've both got good sites.
Here are Jeannie and Riley on one of our walks. I told her she could approve the photo I took before I post it, but this is so good, I hope she won't mind. Two cutie-patooties!
Yesterday we went into town to get some propane for the tank, shop, visit the library and do laundry. So nice to get that all done.
When I was getting ready for bed last night, the fridge started clicking. I couldn't figure out what it was, but knew it was coming from the fridge. Then I noticed the "check" light was on. I fiddled a bit with the buttons, but the clicking kept on. Finally I thought, I wonder if the attendant didn't turn the propane back ON as I had asked him to... I tried to start a burner on the stove top and - no flame. I've never messed with the propane tank, the attendants always fill it and turn it back on for me, as I'm watching. I think the guy misunderstood me, because he was done when I asked if he would turn it on, and he bent over and turned the dial. I think he had already turned it on, and thought I was asking him to turn it off. So anyway, from about 1:00 until 10:00 pm, the propane was off and the freezer was slowly defrosting. Thank goodness I heard the ignition clicking, trying to turn on. All my food is okay.
The reason I didn't use any propane all afternoon/evening was that I bought some hot, home-made tamales that some people were selling to benefit their church. They had a table outside Wal-Mart, and I decided to buy a couple. I'm not tamale person, I think I've had one but really don't remember. Jeanne and I were talking about home-made tamales the day before so I thought I'd take a chance.
I took them back to The Palms which parked in the Wal-Mart parking lot, made some quick guacamole, threw on some sour cream and took a bite. Wow - it was so good! I bought two, but only ate one, packed up the other one in a baggie and went back and bought six more. They are now sitting in my freezer waiting for an evening when I'm craving Mexican food and want an easy dinner. Yummm. I just finished the second one for dinner tonight and it was just as good as the first. Sorry, no photos - it's gone. I'll try to remember to take a picture next time.
But I digress... So, anyway, I hadn't opened the fridge and noticed the light was out, or tried to start the stove or burners, and didn't know I had no propane coming through the lines. I was full from my late lunch and didn't use the kitchen at all last night.
I went outside with a flashlight and checked the propane compartment to see what was what. There was an OPEN/CLOSE knob, so obviously that is what I needed to turn. I turned it to the left, per the OPEN arrow, but it turned more than I thought it should. Even so, I kept turning until it stopped. I have to tell you, I was really nervous messing with the propane.
Then I came back inside and tried a few propane powered appliances and everything was working again.
For any RVing newbies or wannabes, this is how I've learned most things. Now I know how to turn on the propane, and I'll make sure it's ON every time I refill my LPG tank. One more piece of knowledge. It couldn't be easier, or obvious, but I don't like messing with some things and the propane tank is one of those things. I don't want to explode because I did something wrong. I think the OPEN/CLOSE knob is too easy to fail, so don't be afraid to open or close it.
This morning I checked with Jeanne, since she also has a built-in LPG tank, and she said yes, that's all I needed to do. She added that when you turn the knob, turn it all the way ON or all the way OFF. Don't go halfway, it's not good for the system.
Katie and I spent a little more than a week at Manzano Mountain State Park in New Mexico. This was our first visit to this park, which has been closed for a few years because of fire danger. It's not near anything, so if you visit it, make sure you have enough food and supplies. They do have a dump site and water and trash removal, and a very nice couple who are camp hosting.
It's a small park nestled in trees - the electric sites are on a loop. Most of the reserved electric sites are nice and looked level, but the non-reserved sites were pretty small and not level. I circled the loop three times when we arrived looking for a good non-reserved spot, but couldn't find one, and the overflow area was chained off. A park employee told me it was closed because a group of 150 (boy scouts and their families) were expected for the weekend and they didn't want anyone parking in their spaces. The camp hosts were in Albuquerque for the day, and so I took an open "one-night only reserved" dry site and talked to the camp host later that day.
He said he would have the Ranger take down the chain so I could get back into the overflow primitive area and choose a site at the end. There were seven sites in an end loop that were available for campers not attending the group events.
This is our site for the first night. Nice and level, nice shade structure with a table and a fire ring.
The next morning we went back into the overflow area - I liked that area better, but there were only two sites with enough sun to keep my solar panels in the sunshine all day, and this is the one I chose. I actually had sun on the roof all day long - it was a very good site. Kind of hard to back into, though, without hitting trees. It was pretty level. We were NOT in the desert any more!
Below is Katie checking out the lizard hole, and of course, I made her get away from it. I didn't want her getting bit on the nose.
I was planning to stay here the full 14 days, but one morning I woke up and decided to leave, and 2 hours later, we were at Costco in Albuquerque.
It was really nice to camp in the forest with the great pine tree smell, but I think Manzano Mountains State Park, as nice as it is, was just too small for us, and the spaces, even the overflow spaces, were too close together. With that many trees it's hard to find a spot that will work with the solar panels. This park is pretty out-of-the way, too, and I don't think I'll use the gas to come back next year. It's an about a two hour drive to Albuquerque. I'm glad we visited this once, though.
By the way, the 150 campers with the Boy Scout troups were so quiet, even though they were right next to us I hardly knew they were there. They were always working in small groups at different campsites, earning badges I think. It was a pleasure to camp near them. I honestly don't know how they kept those boys so quiet for the whole weekend. Some of the families had small children, too. Super well behaved!
At first I thought it was a dove, but my field guide shows that the Band-tailed Pigeon has that iridescence on it's neck, and yellow legs and feet.
I ordered jewelry from my Granddaughter, Kennedy's business, Touched by Charms, which is a part of Origami Owl Custom Jewelry. I had two orders, first from Kennedy's Launch Party and then from the Jewelry Bar party that my daughter, Kristy had. Daughter-in-law Trish and Kennedy flew up to Northern California to Kristy's house to host the party. I ordered two lockets and some charms and they are HERE.
There are really beautiful - and you can check out what is available and see good photos of all the lockets, charms, etc., HERE. Unfortunately my photos aren't that good, but you'll get an idea and see what I've ordered. I have to say, I just LOVE the small locket.
I wanted a medium gold locket and a mini rose gold locket - which is my favorite. I decided not to purchase a rose gold chain, thinking I could put either locket on the gold chain I wear every day. After wearing the small rose gold locket for a week or so, I've decided to get a rose gold chain. I don't think it looks as good on the yellow gold chain. I've put the dangling heart charm on my chain, too. It looks really good!
The round circle with the heart goes into the gold locket, and I think I want a yellow "plate" of some sort, too.
I can fit three charms into the mini rose gold locket, so I put in the saguaro palm (for living in the desert most of the time), the small heart - just because, and the dog paw charm (for Katie). I can switch them out with the cross (for my faith) and the "blog" (that's obvious), and the palm tree (for The Palms, and also because I most recently lived in San Diego, where there are a lot of palm trees).
They will all fit inside the medium sized locket, or the large size, which I didn't get. In the photo below, I just noticed I have the palm tree backwards, oops.
I have a little shopping list that is growing - one rose gold chain and a few more charms and one gold plate. They are offering new items all the time, so I'll check their website from time to time until I have enough to order. The charms are only $5, so it's pretty easy to add to my collection.
WELCOME to our two newest Follows: Rebekah Ketterman, and Jan. Neither has any information to pass on to you, but if they let me know about blogs, etc., I'll do that.
I have a bunch of photos of Katie and I'm just going to publish them all here. Sometimes she is pretty funny.
Here's Katie snuggling her "baby" while she rests.
I had a large rawhide chew toy and she worked on it for two days. She usually stays right on her blanket when she eats treats or plays with her toys, but she was pretty excited about this big chew toy and I had to move her back onto her blanket.
Here she is playing with her ball.
Little by little it went down until it fell on the floor and she forgot about it. Sometimes she'll toss it around by herself.
Walking along the concrete berm.
Here's Katie sleeping. She often sleeps on her toys, this time she's got her ball with her. That can't be very comfortable!
Her ankle is still swelling up, and on this morning I taped it up before our walk to see what happened. It didn't really do much good. See her right rear ankle with the tape?
Waiting for a treat after our walk. She's pretty patient - I always hang up her leash and put the camera away, etc. while she waits. A few times I've gotten busy and forgotten her treat, but she'll stare me down until I remember. Look at that tail wagging!
Here we are in shadow as the late afternoon sun goes down.
Katie loves her walks - whether on foot or in the stroller. We've been walking a lot more since we have the stroller - usually we'll take a walk on foot in the morning, and by the afternoon we're using the stroller. I keep an eye on her ankle, but she seems okay. I'm not going to take her to the vet until her regular appointment in July, then we'll have it checked out during her annual exam.
Oh, poor thing, she at it!


These solid aluminum rings feature unique no-weld construction. They are 5 mm thick, nickel-free and thus hypoallergenic. The rings are sold in pairs and they are available in a wide variety of colours to match any DIDYMOS wraps.


Love should be shown everyday, but today serves us well as a reminder to slow down once and awhile and remember everyone around us who has helped shape our lives for the better.
Happy Valentine's Day everyone! I love you so much Ollie, and your Daddy too! I am so lucky to be loved so much, by the both of you.


When your customers contact you with at GDPR request, you may have to provide their complete profile data, or a record of their profile deletion.
Navigate to the profile you want to export and click Export Profile. A modal appears prompting you to confirm your export. Click Start Export to export the profile data.
A green callout confirms that your profile data is available on your Downloads page. From your Downloads page, click to Download to export your profile data as a .zip file.
When responding to a GDPR deletion request, you can choose to add a profile to your account's list of deleted profiles, preserving a record that your business has complied with the deletion request.
When deleting a profile in Klaviyo, the profile's event history as well as every recipient record associated with the profile remain in Klaviyo, but any sensitive data associated with the profile is removed.
For a profile's event history, any customer specific information is replaced with the word "redacted", and then the customer's information is permanently deleted from Klaviyo.
For every recipient record associated with a profile, the profile's email address is replaced with a random email address that starts with the word "redacted", for example, redacted.11bab993-58bb-4cc9-a654-c152dbe009e1@example.com.
To delete a Klaviyo profile, navigate to the profile and click the Delete button below the Information section of a contact profile. After clicking the Delete button, a warning message prompts you to confirm your decision to delete a profile.
Check the box on the delete profile warning message and the deleted profile will be added to your account's list of deleted profiles.
After clicking Delete Person you're taken to your list of deleted profiles. This list provides a permanent record that your business has complied with any GDPR deletion requests.
You can access your list of deleted profiles by navigating to to your Account > Settings > Account Maintenance and clicking the link.


PASADENA – It wasn't the acrimonious – and unresolved – three-year debate over the $1.1 million Pasadena Convention Center sculptures that prompted the city to take a fresh look at its public art policy, said Rochelle Branch, Pasadena's cultural affairs manager.
But there's not much debate that the squabble between preservationists and art-on-the-plaza proponents made public art a hot topic.
Now, the city is embarking on its first Public Art Master Plan officials say will "define a new vision, goals and locations" for art in public spaces.
The process, starting next week with the first of three neighborhood meetings, aims to collect community input and opinion, similar to how the Cultural Nexus plan came together in 2005, Branch said.
When: 6 p.m. Nov. 9 at Victory Park, 2575 Paloma St.
Where: Pasadena Central Library, 285 E. Walnut St.
Where: Armory Center for the Arts, 145 N. Raymond Ave.
But "a lot has changed in the last five years, and the Cultural Nexus Master Plan was never specifically geared to public art, it was not an element of the plan," Branch said.
"I think what's changed is that there's much more community visibility for public art in general and for the program in particular, and that's a good thing. Now we're looking for more public engagement, and that's why we're having community meetings," she added..
Dale Oliver, chairman of the Arts and Culture Commission's public art sub-committee, said there would be a thorough inquiry into the city's public art process before the final plan is sent to the City Council for review.
He cited the long-stalled plans for the Convention Center plaza art – the "Thinking Caps" sculpture by New York artist Dennis Oppenheim and the 38-by-15-foot kinetic sculpture by Hans Peter Kuhn – as an example of what can happen when there's a "failure to have consensus" in the community.
The new plans to put rotating exhibits on sites throughout the city, Oliver said, is one way of "introducing the spirit of public art in Pasadena in a way that's not a permanent commitment and won't invoke the same heated debate" as the plaza project did.
"I would see, out of this process, that we would have some specific program to put forward, as well as a general statement of what we're looking for in the future, in terms of a public art experience and as a city," Oliver said. "Specific guidance" should also be given to developers, he said.
Terry LeMoncheck, executive director of the Pasadena Arts Council, said the public art master plan is "timely" and necessary.
There's room in the city for "multiple expressions of public art, both rotating and permanent, iconic sculptures or some other kind," she said.
Branch said everyone is being invited to participate – those who have long been involved in the public art debate and the "broader section" whose voices may not have been heard.
"They will play a key role in determining the future of public art, and being part of the vision for the future," she said.


A hand-woven trivet set. Bears the intrecciato weave inspired by Greek motif. Light & dark tones of beige with gold, along with the play of textured versus plain brings out the mat effect on leather, literally two-times fold.


The scene opens at the end of the 19th century.
The Château of Amboise rises through a misty nightscape of interlace swirls, like the wandering shadow of a past full of buried splendour.
Silhouettes glimpsed in the maze-like scene move stealthily along the wooden scaffolding that is part of the vast renovation works recently begun around the building.
As often happens, drawn in by the awe-inspiring remains of the castle that act as guardians of its centuries-old history, a few children have disobeyed their parents and have come here to dream of battles and valiant knights and conquests.
Suddenly, there is a cry : one of the little group of adventurers has just bumped into one of the beams of the construction works. He falls backwards in a daze as his playmates mockingly compare him to the unfortunate Charles VIII who, 400 years before, died tragically on this exact spot.
Then suddenly, the storytelling plunges back to historic times : to the Court of Charles VIII. The chidren's dreams become strangely real and resurrect, in full view of the amazed spectators, the pomp and plots of the Royal Court at the dawn of the French Renaissance. In the shadows is Louise de Savoie, a woman working secretly to see that the great royal destiny promised to her son by a mysterious hermit should come about - the accession to the throne of Francois I is soon to become reality !
Though centuries apart, "The Amboise Prophecy" alternates from one era (the 1900's) to another (that of Louise de Savoie), pushing back the known limits of light & sound spectaculars in a magical combination of multimedia arrangements and epic scenes.
The completely new show created by Animation Renaissance Amboise transports the spectators into an innovative multi-sensorial arena of dazzling images, optical illusions and special effects that interact with over 250 performers on stage.


text:
Q: Include .shtml file in a .php page? I'd like to include a .shtml file in a .php page. I know it is possible, as i have done it before (though i've forgotten the code). Please can someone help?

A: I would suggest using:
include_once('/includes/header.shtml');

A: Did you try :
 echo file_get_contents('/includes/header.shtml');
 // or
 echo file_get_contents('includes/header.shtml');

It might work.
But I would just start converting all the templates to any of available php templating engines.

redpajama_set_name: RedPajamaStackExchange


The fake "Barrister Emethoa Samuel" email below is a scam. Recipients are asked to delete it and should not follow the instructions in it. The fake email is being sent by scammers who are attempting to trick their potential victims into sending them money or personal information by impersonating someone else. An advance-fee scam is a form of fraud and one of the most common types of confidence trick. The scam typically involves promising the victim a significant share of a large sum of money, in return for a small up-front payment, which the fraudster requires in order to obtain the large sum.
Compliment of the day to you!..
I am Barrister Emethoa Samuel a lawyer to Mr. Jorge, a gold merchant, who was my client before he died as a result of lung cancer.
US$10.5Million (Ten million five hundred thousand united states dollars) into your account. I will give you full details of this transaction immediately you notify me your interest.